Timezones and Daylight saving in .NET round trip to server
我们的应用程序遇到一个问题,在特定的TimeZone中有一个特定日期,在从服务器到客户端,然后从客户端到服务器的往返中,不保留DateTime的值。这在巴西利亚时区("E.南美标准时间")中观察到,并且DateTime值是"1984-11-04 00:00:00"。
我能够使用以下代码重现此问题:
1 2 3 | DateTime d = new DateTime(1984, 11, 4, 0, 0, 0, DateTimeKind.Local); var dUtc = d.ToUniversalTime(); var dRtLocal = dUtc.ToLocalTime(); |
dUTC的最终值是"1984-11-04 03:00:00"(正确),dRtLocal是"1984-11-04 01:00:00"(不太正确)。
我发现虽然巴西的夏令时只在1985年开始,但Windows的日期从0001-01-01到2006-12-31也有相同的规则,根据这个规则,夏季时间将从这个确切的日期开始(1984-11 -04 00:00:00)将时钟向前移动1小时。
除了这个时区的DST规则错误之外,我还发现了TimeZone和TimeZoneInfo类(GetUtcOffset,IsAmbiguousTime,IsInvalidTime)方法的其他一些奇怪的行为和不一致的结果。
举个例子(我的电脑的当前时区设置为"E.南美标准时间"):
1 2 3 4 5 | TimeZone.CurrentTimeZone.GetUtcOffset(new DateTime(1984,11,03,23,00,00, DateTimeKind.Local)) returns -02:00 TimeZoneInfo.FindSystemTimeZoneById("E. South America Standard Time").GetUtcOffset(new DateTime(1984,11,03,23,00,00, DateTimeKind.Local)) returns -03:00 |
在第一种情况下,似乎它正在使用当年的DST规则并将它们应用到1984年(2015年夏季时间将从2015-10-18开始)。第二个似乎在Windows中应用此时区的DST规则。
除了使用和存储UTC中的所有日期之外,还有哪些解决方法可以避免这些问题?
在将DST规则应用于DST规则与当前年度不同的过去日期时,真的是一个错误吗?
更新@ matt-johnson回答之后我做了一些测试,发现了与无效DateTime相关的更多不一致行为。
正如马特指出的那样,有关日期是无效日期(根据Windows规则)。但是如果运行:
1 | var isInvalid = TimeZoneInfo.FindSystemTimeZoneById("E. South America Standard Time").IsInvalidTime(new DateTime(1984, 11, 4, 0, 0, 0, DateTimeKind.Local)) |
结果是假的,即使通过Windows DST规则应被视为无效。但如果运行:
1 | var isInvalid2 = TimeZoneInfo.Local.IsInvalidTime(new DateTime(1984, 11, 4, 0, 0, 0, DateTimeKind.Local)) |
结果现在是真的。请注意,我当前的TimeZone是"E。南美标准时间"(TimeZoneInfo.FindSystemTimeZoneById("E。南美标准时间")。StandardName == TimeZoneInfo.Local.StandardName为true)。
尝试使用TimeZoneInfo.ConvertTimeToUtc将DateTime转换为UTC会抛出Matt指出的异常
使用
The
TimeZone class supports only a single daylight saving time adjustment rule for the local time zone. As a result, theTimeZone class can accurately report daylight saving time information or convert between UTC and local time only for the period in which the latest adjustment rule is in effect. In contrast, theTimeZoneInfo class supports multiple adjustment rules, which makes it possible to work with historic time zone data.
您应该考虑不推荐使用
关于转换不匹配,当您在
请考虑不是调用
1 | var dUtc = TimeZoneInfo.ConvertTimeToUtc(d, TimeZoneInfo.Local); |
您可能认为这是等效的,但此代码会引发异常,因为DST转换已跳过
最后 - 正如您所指出的那样,巴西的1984年时区与Windows包含的最早的2006年时区数据不同。通常,Windows时区不是历史信息的良好来源。相反,您应该考虑使用TZDB时区,其历史至少为1970年,在许多情况下更早。在.NET中,您可以使用Noda Time库执行此操作。等效区域
但是,仍然意识到即使使用Noda Time,您也无法绕过无效的本地日期/时间。如果它在本地时区无效,则从utc到local的转换永远不会产生该结果。