.NET是否具有时区更改的历史记录?

时间:2014-10-02 07:38:10

标签: .net datetime timezone

我们的政府喜欢改变当地时间或禁用夏令时。

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来解决问题?

更新

找到了课程TimeZoneInfo和相关的AdjustmentRule课程。留下来测试TimeZoneInfo.Local时区的自定义是否会影响DateTime API。

更新2: 似乎UTC偏移量不会存储为历史记录,而AdjustmentRule仅会更改一年中的白天时间。

3 个答案:

答案 0 :(得分:7)

.NET跟踪某些历史记录,但并不总是准确的。你偶然发现了一个不准确之处。

.NET通过注册表从Windows导入所有时区信息,如herehere所述。如果你在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\Russian Standard Time\Dynamic DST查看注册表,你会发现它只跟踪此时区2010年前进的信息。 2000年的测试日期不会很好,因为它将回溯到最早的规则(2010年)。

在注册表中跟踪基础UTC偏移信息 ,但在.NET导入它的AdjustmentRule类中。如果您检查此时区的调整规则,您会发现根本不会导入2012和2013:

var tz = TimeZoneInfo.FindSystemTimeZoneById("Russian Standard Time");
foreach (var rule in tz.GetAdjustmentRules())
{
    Console.WriteLine("{0:d} - {1:d}", rule.DateStart, rule.DateEnd);
}

输出:

1/1/0001 - 12/31/2010
1/1/2011 - 12/31/2011
1/1/2014 - 12/31/2014

即使它们存在于Windows注册表中,也不会导入2012和2013,因为它们没有夏令时调整。

当基本偏移发生变化时会产生问题 - 就像它对此时区一样。由于它目前是+3,并且未导入+4的两年,因此对于那些失踪年份,它看起来会是+3。

使用TimeZoneInfo没有好的解决方案。即使您尝试创建自己的自定义时区,也无法在可用的数据结构中进行此类更改。

幸运的是,还有另一种选择。您可以通过IANA time zones库使用标准Noda Time

以下代码使用Noda Time来匹配您在原始代码中编写的内容:

DateTimeZone tz = DateTimeZoneProviders.Tzdb.GetSystemDefault();
Console.WriteLine(Instant.FromUtc(2012, 1, 1, 0, 0).InZone(tz).LocalDateTime);
Console.WriteLine(Instant.FromUtc(2012, 6, 1, 0, 0).InZone(tz).LocalDateTime);
Console.WriteLine(Instant.FromUtc(2000, 1, 1, 0, 0).InZone(tz).LocalDateTime);
Console.WriteLine(Instant.FromUtc(2000, 6, 1, 0, 0).InZone(tz).LocalDateTime);

如果您的当地时区尚未设置为莫斯科,则可以将第一行更改为:

DateTimeZone tz = DateTimeZoneProviders.Tzdb["Europe/Moscow"];

输出:

1/1/2012 4:00:00 AM
6/1/2012 4:00:00 AM
1/1/2000 3:00:00 AM
6/1/2000 4:00:00 AM

更新

上面描述的AdjustmentRule未跟踪基本偏移量更改的问题在Microsoft支持文章KB3012229中有所描述,随后在.NET Framework 4.6和.NET Core中进行了修复。

the reference sources中,可以看到AdjustmentRule现在保留m_baseUtcOffsetDelta字段。虽然此字段不是通过公共属性公开的,但它确实考虑了计算,如果您使用FromSerializedStringToSerializedString方法(如果有人实际使用这些方法),它确实反映在序列化中。< / p>

答案 1 :(得分:2)

答案 2 :(得分:2)

历史时区数据非常复杂,并且填充了许多适用于特定地理区域的小例外情况的例子,这些例外目前没有简单的描述方式。不仅偏移在变化,而且它们应用的区域也在变化。

有一个项目来模拟来自世界各地的这些数据的历史记录:

.NET不支持开箱即用的功能。对问题进行一般性解决方案将非常困难,但是如果您只需要在一小部分区域内进行解决,那么您应该能够使用TimeZoneInfo class自己填充这个,这些文档的状态为:

  

TimeZoneInfo类的成员支持以下操作:

     
      
  • 创建尚未由操作系统定义的新时区
  •