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

Pav*_*nin 18 .net timezone datetime

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

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());
Run Code Online (Sandbox Code Playgroud)

是否存在一些额外的API来应对这个问题?

更新:

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

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

Mat*_*int 9

.NET跟踪一些历史记录,但是它并不总是准确的。您偶然发现了其中一种错误。

.NET通过注册表从Windows导入所有时区信息,如此此处所述。如果您查看注册表,HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\Russian Standard Time\Dynamic DST则会发现它仅跟踪该时区从2010年开始的信息。2000年的测试日期不能很好地起作用,因为它将追溯到最早的可用规则(2010年)。

基地UTC偏移信息跟踪在注册表中,而不是AdjustmentRule类.NET进口成。如果查看此时区的调整规则,则会发现根本没有导入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);
}
Run Code Online (Sandbox Code Playgroud)

输出:

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

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

当基本偏移量更改时,这会产生问题-就像该时区一样。由于当前为+3,并且未输入原为+4的两年,因此对于那些缺失的年份,它看起来像为+3。

使用此方法没有好的解决方案TimeZoneInfo。即使您尝试创建自己的自定义时区,也很难将这种变化适合可用的数据结构。

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

以下代码使用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);
Run Code Online (Sandbox Code Playgroud)

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

DateTimeZone tz = DateTimeZoneProviders.Tzdb["Europe/Moscow"];
Run Code Online (Sandbox Code Playgroud)

输出:

1/1/0001 - 12/31/2010
1/1/2011 - 12/31/2011
1/1/2014 - 12/31/2014
Run Code Online (Sandbox Code Playgroud)

更新资料

AdjustmentRuleMicrosoft支持文章KB3012229中描述了上述不跟踪基本偏移量更改的问题,随后在.NET Framework 4.6和.NET Core中修复了该问题。

参考资料中,可以看到AdjustmentRule现在保留了一个m_baseUtcOffsetDelta字段。尽管此字段不是通过公共属性公开的,但它确实会影响计算,并且如果您使用FromSerializedStringand ToSerializedString方法(如果有人实际使用那些方法),它的确会反映在序列化中。