Dot-Net

.NET 是否有時區更改的歷史記錄?

  • October 2, 2014

我們的政府喜歡更改當地時間或啟用|禁用夏令時。

MS 為俄羅斯部署了更新檔,以考慮新的時間變化。

現在的問題是,這些變化的歷史是否存在?

當我在 01.01.2000 系統中獲得 UTC 時間時,應該記住莫斯科時區有 +3 UTC。(夏天+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僅在一年中更改日光時間。

.NET 跟踪了一些歷史,但並不總是準確的。您偶然發現了其中一個不准確之處。

.NET 通過系統資料庫從 Windows 導入所有時區資訊,如此此處所述。如果您查看系統資料庫,您會發現它只跟踪從 2010 年開始的該時區的資訊。2000 年的測試日期不會很好,因為它將退回到最早的可用規則(2010 年)。HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\Russian Standard Time\Dynamic DST

基本 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);
}

輸出:

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. 即使您嘗試創建自己的自定義時區,也很難將這種更改融入可用的資料結構中。

幸運的是,還有另一種選擇。您可以通過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);

如果您的本地時區尚未設置為莫斯科,您可以將第一行更改為:

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

更新

Microsoft 支持文章KB3012229中描述了上述AdjustmentRule不跟踪基本偏移更改的問題,隨後在 .NET Framework 4.6 和 .NET Core 中得到修復。

參考資料中,可以看到AdjustmentRule現在保留了一個m_baseUtcOffsetDelta欄位。雖然此欄位不是通過公共屬性公開的,但它確實會影響計算,並且如果您使用FromSerializedStringandToSerializedString方法(如果有人實際使用這些方法),它確實會反映在序列化中。

引用自:https://stackoverflow.com/questions/26156260