DataContractJsonSerializer DateTime隐式时区转换

本文关键字:时区 转换 DateTime DataContractJsonSerializer | 更新日期: 2023-09-27 17:50:12

我在数据库中有一个日期时间,我使用实体框架从数据库检索它,然后我通过DataContractJsonSerializer通过JSON API传递数据。

在DataContractJsonSerializer中处理时,date time字段中的时间似乎已经根据服务器的本地时区进行了调整。epoch表示的时间比预期时间提前1小时。日期时间类型是UTC,但以前它是未指定的,我有同样的问题。

在我的应用程序中,我希望在客户端而不是服务器端显式地在时区之间进行转换,因为这样更有意义。我对这种隐式功能感到惊讶,因为我的日期时间值应该是简单的值,就像整数一样。

谢谢

DataContractJsonSerializer DateTime隐式时区转换

DataContractJsonSerializer将输出时区部分(+zzzz),如果您的DateTime。类型等于本地或未指定。此行为与XmlSerializer不同,后者仅在Kind等于未指定时输出时区部分。

如果好奇的话,可以查看JsonWriterDelegator的源代码,其中包含以下方法:

 internal override void WriteDateTime(DateTime value) 
    {
        // ToUniversalTime() truncates dates to DateTime.MaxValue or DateTime.MinValue instead of throwing 
        // This will break round-tripping of these dates (see bug 9690 in CSD Developer Framework)
        if (value.Kind != DateTimeKind.Utc)
        {
            long tickCount = value.Ticks - TimeZone.CurrentTimeZone.GetUtcOffset(value).Ticks; 
            if ((tickCount > DateTime.MaxValue.Ticks) || (tickCount < DateTime.MinValue.Ticks))
            { 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 
                    XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.JsonDateTimeOutOfRange), new ArgumentOutOfRangeException("value")));
            } 
        }
        writer.WriteString(JsonGlobals.DateTimeStartGuardReader);
        writer.WriteValue((value.ToUniversalTime().Ticks - JsonGlobals.unixEpochTicks) / 10000); 
        switch (value.Kind) 
        { 
            case DateTimeKind.Unspecified:
            case DateTimeKind.Local: 
                // +"zzzz";
                TimeSpan ts = TimeZone.CurrentTimeZone.GetUtcOffset(value.ToLocalTime());
                if (ts.Ticks < 0)
                { 
                    writer.WriteString("-");
                } 
                else 
                {
                    writer.WriteString("+"); 
                }
                int hours = Math.Abs(ts.Hours);
                writer.WriteString((hours < 10) ? "0" + hours : hours.ToString(CultureInfo.InvariantCulture));
                int minutes = Math.Abs(ts.Minutes); 
                writer.WriteString((minutes < 10) ? "0" + minutes : minutes.ToString(CultureInfo.InvariantCulture));
                break; 
            case DateTimeKind.Utc: 
                break;
        } 
        writer.WriteString(JsonGlobals.DateTimeEndGuardReader);
    }

我在我的机器上运行了以下测试

var jsonSerializer = new DataContractJsonSerializer(typeof(DateTime));
var date = DateTime.UtcNow;
        Console.WriteLine("original date = " + date.ToString("s"));
        using (var stream = new MemoryStream())
        {
            jsonSerializer.WriteObject(stream, date);
            stream.Position = 0;
            var deserializedDate = (DateTime)jsonSerializer.ReadObject(stream);
            Console.WriteLine("deserialized date = " + deserializedDate.ToString("s"));
        }

产生预期输出:

original date = 2011-04-19T10:24:39
deserialized date = 2011-04-19T10:24:39

因此在某些时候你的日期必须是未指定的或本地的。

从DB中取出后,通过调用

将类型从Unspecified转换为Utc
 entity.Date = DateTime.SpecifyKind(entity.Date, DateTimeKind.Utc);

不要忘记将SpecifyKind的返回值赋给你的对象就像我写的