我们的政府喜欢改变当地时间或启用夏令时 .
MS为俄罗斯部署补丁以考虑新的时间变化 .
现在有一个问题,即变化的历史是否存在?
当我在01.01.2000系统中获得当天的UTC时间时,系统应该记住莫斯科时区有3 UTC . (在夏天4)那一刻 .
对于2012年1月1日,我们在冬季和夏季都有4 UTC . 很快我们将有3 UTC .
简单的测试表明,.NET不会保留有关更改的记录:
var t = new DateTime(2012,1,1);
// UTC +4 expected
System.Console.WriteLine(t.ToLocalTime());
// UTC +4 expected
t = new DateTime(2012,06,1);
System.Console.WriteLine(t.ToLocalTime());
// UTC +3 expected
t = new DateTime(2000,1,1);
System.Console.WriteLine(t.ToLocalTime());
// UTC +4 expected
t = new DateTime(2000,6,1);
System.Console.WriteLine(t.ToLocalTime());
是否存在一些额外的API来应对这个问题?
Update:
找到了类TimeZoneInfo和相关的AdjustmentRule类 . 留下来测试 TimeZoneInfo.Local
时区的自定义是否会影响DateTime API .
Update 2: 似乎UTC偏移不会存储为历史记录, AdjustmentRule
仅会更改一年中的白天时间 .
3 回答
.NET跟踪一些历史记录,但并不总是准确的 . 你偶然发现了一个不准确之处 .
.NET通过注册表从Windows导入所有时区信息,如here和here所述 . 如果您在
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\Russian Standard Time\Dynamic DST
查看注册表,您会发现它只跟踪此时区2010年前进的信息 . 2000年的测试日期不会很好,因为它将回溯到最早的规则(2010年) .基础UTC偏移信息在注册表中进行跟踪,但不在.NET将其导入的
AdjustmentRule
类中进行跟踪 . 如果您检查此时区的调整规则,您会发现2012年和2013年根本没有导入:OUTPUT:
即使它们存在于Windows注册表中,也不会导入2012和2013,因为它们没有夏令时调整 .
当基本偏移发生变化时,这会产生问题 - 就像它对此时区一样 . 由于它目前是3,并且它是4的两年没有进口,那么对于那些失踪的年份它看起来是3 .
使用
TimeZoneInfo
没有很好的解决方案 . 即使您尝试创建自己的自定义时区,也无法在可用的数据结构中进行此类更改 .幸运的是,还有另一种选择 . 您可以通过Noda Time库使用标准IANA time zones .
以下代码使用Noda Time来匹配您在原始代码中编写的内容:
如果尚未为莫斯科设置本地时区,则可以将第一行更改为:
OUTPUT:
更新
上面描述的
AdjustmentRule
跟踪基本偏移量更改的问题在Microsoft支持文章KB3012229中进行了描述,随后在.NET Framework 4.6和.NET Core中进行了修复 .在the reference sources中,可以看到
AdjustmentRule
现在保持m_baseUtcOffsetDelta
字段 . 虽然此字段不是通过公共属性公开的,但它确实考虑了计算,如果使用FromSerializedString
和ToSerializedString
方法(如果有人实际使用这些方法),它确实反映在序列化中 .你可以试试NodaTime,
http://nodatime.org/
什么是更好的解释
http://blog.nodatime.org/2011/08/what-wrong-with-datetime-anyway.html
历史时区数据非常复杂,并且充满了许多适用于特定地理区域的小例外情况的例子,这些例外今天没有简单的描述方式 . 不仅偏移在变化,而且它们应用的区域也在变化 .
有一个项目来模拟来自世界各地的这些数据的历史:
http://www.iana.org/time-zones
http://en.wikipedia.org/wiki/Tz_database
.NET不支持开箱即用的功能 . 对问题进行一般解决方案将非常困难,但是如果您只需要在一小部分区域内进行解决,那么您应该能够使用TimeZoneInfo class自己填充这个,这些文档的状态如下: