本地与托管站点TimeZoneInfo转换新西兰时区时间结果不一致排查
问题原因分析
这个差异的核心在于JavaScriptSerializer序列化DateTime时的行为依赖于DateTime的Kind属性,以及TimeZoneInfo.ConvertTimeFromUtc方法的返回值特性:
TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, nzZone)返回的DateTime对象,其Kind属性是DateTimeKind.Unspecified——该方法仅转换时间数值,不会为结果附加时区信息标记。- JavaScriptSerializer对
Unspecified类型的DateTime处理逻辑是:将其视为当前机器的本地时区时间,再转换为UTC时间生成时间戳。
本地运行场景
你的本地开发机时区大概率是UTC(或者与UTC偏移为0),当JavaScriptSerializer处理这个Unspecified的新西兰时间时,会把它当作UTC时间,因此序列化后的时间戳和DateTime.UtcNow完全一致——这其实是个巧合的错误结果。
托管服务器场景
托管站点的服务器时区不是UTC,也不是新西兰时区(比如常见的美国东部时区UTC-5)。此时JavaScriptSerializer会把这个Unspecified的新西兰时间(比如2018-05-21 14:16:01,Utc+12)当成服务器本地时区的时间,再转换为UTC。例如:
- 服务器时区是UTC-5,那么它会把
2018-05-21 14:16:01视为UTC-5时间,转换为UTC就是2018-05-21 19:16:01,对应的时间戳自然比原UtcNow大了17小时的毫秒数,最终显示为第二天的日期。
解决方法
要避免这种依赖机器时区的不确定行为,推荐使用DateTimeOffset来处理带时区偏移的时间,它能明确携带时区信息,JavaScriptSerializer会正确序列化其对应的UTC时间戳:
JavaScriptSerializer js = new JavaScriptSerializer(); TimeZoneInfo nzZone = TimeZoneInfo.FindSystemTimeZoneById("New Zealand Standard Time"); DateTime utcNow = DateTime.UtcNow; DateTime nzTime = TimeZoneInfo.ConvertTimeFromUtc(utcNow, nzZone); // 创建带新西兰时区偏移的DateTimeOffset DateTimeOffset nzTimeOffset = new DateTimeOffset(nzTime, nzZone.GetUtcOffset(nzTime)); litUTC.Text = $"{utcNow.ToString()} \"{js.Serialize(utcNow)}\" \"{js.Serialize(nzTimeOffset)}\"";
这样无论服务器时区是什么,序列化后的新西兰时间戳都会是UtcNow加上新西兰时区偏移的正确数值,和你预期的一致。
内容的提问来源于stack exchange,提问作者loveprogramming
相关产品推荐
相关产品推荐

