2014-10-02 107 views
11

我们的政府喜欢更改当地时间或禁用夏令时。
.NET是否有时区更改的历史记录?

MS部署的补丁为俄罗斯考虑新的时间变化。

现在有一个问题,是否存在变化的历史?

当我在01.01.2000获得当天的UTC时间时,系统应记住莫斯科时区的UTC为+3。 (夏季+4)。

对于01.01.2012,我们在冬季和夏季都有+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变化只有白天的时间。什么是

+0

看看'DateTimeOffset'。 ***如果您可以在捕获数据时捕获时区偏移量,则可以更好地显示它并根据它进行计算。 – 2014-10-02 18:18:37

回答

4

.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); 
} 

OUTPUT:

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库。

下面的代码使用野田的时间来匹配你在你原来的代码中写道:

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"]; 

OUTPUT:

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上面描述的AdjustmentRule未跟踪基址偏移量更改的问题在Microsoft支持文章KB3012229中进行了描述,随后在.NET Framework 4.6和.NET Core中进行了修复。

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

+0

几天前有些事情发生了变化。现在2012年和2013年进口。至少在.NET 4.5.2(Win8.1,Win10)中 1/1/0001 - 12/31/2010 1/1/2011 - 12/31/2011 1/1/2012 - 12/31/2012 1/1/2013 - 12/31/2013 1/1/2014 - 12/31/2014 但2015和2016的数据在哪里? – 2016-03-02 09:26:56

+0

2014年的最后一次俄罗斯变化减少了夏令时,所以2015年及以后没有调整规则,因为没有调整;偏移是固定的。列表中最后一条规则的最后一个偏移量应该始终适用于该时间。 – 2016-03-02 17:50:56

+0

另外,虽然你的目标是4.5.2,但我猜你已经安装了4.6,因此它使用[KB3012229]的修补程序(https://support.microsoft.com/en-us/kb/3012229 )在4.6中实现(KB仍需要更新以反映这一点)。副作用是,您现在可以看到2012年和2013年的过渡规则,因为它现在正确跟踪基本偏移更改。 – 2016-03-02 17:51:15

2

历史时间段数据是非常复杂的,充满了适用的小例外的例子很多在今天没有简单描述的特定地理区域。偏移量不仅在变化,而且它们所应用的区域也在变化。

不存在对该数据的历史,从世界在这里建模项目:

.NET不支持你想出来的是什么框。使解决问题的一般解决方案将是非常困难的,但是如果你只需要在一个小区域的集合,那么你应该能够填充这个自己使用TimeZoneInfo class,对于该文件规定:

在的TimeZoneInfo类支持的成员如下操作:

  • 创建一个新的时区是不是已经由操作系统定义