The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

釐清觀念:.NET 日期結構(DateTime) 與 時區轉換

昨晚為了將 AD 中的日期屬性讀出來費了好大一番功夫,由於 AD 中的 Lockout-Time Attribute 格式為長度 8 bytes 的長整數型別,時間單位以 100ns 為單位,日期從 UTC 時間的 1601/1/1 00:00:00 起算,然而將時間轉出來後卻突然被時區轉換的問題給搞亂了,因此也趁機將 DateTime 研究了一番。

首先,我先透過 Active Directory Explorer 查出該屬性的時間為 2010/4/20 上午 06:24:20 (如上圖),但不管怎麼轉換時區都無法用漂亮的方法轉換成我本地的時區,有時還會差異到 16 小時之多,我當時搞混的地方在於當我建立 DateTime 時在指定 DateTime.Kind 屬性後,輸出的結果是一模一樣的 ( 2010/4/19 下午 10:24:20 ),然後多轉換幾次之後頭腦就昏了。如下程式:

DateTime d1 = (new DateTime(1601,1,1,0,0,0, DateTimeKind.Local)).AddTicks(129161894600456800L);
DateTime d2 = (new DateTime(1601,1,1,0,0,0, DateTimeKind.Utc)).AddTicks(129161894600456800L);

可能是寫程式寫太晚,又多測了好幾個帳號,時間感就整個亂掉了,所以白花了好多時間查問題,到現在寫這篇文章時感覺好像在說廢話一樣,看文章的人也可能會覺得「不是本來就是這樣嗎?」,但是寫 Code 的當下基本上就是一整個腦袋打結。 :-p

上述程式若執行 d1.ToString() 輸出結果都會是:2010/4/19 下午 10:24:20

  • 如果我們執行 d1.ToLocalTime() 輸出的結果會是 2010/4/19 下午 10:24:20
  • 如果我們執行 d1.ToUniversalTime() 輸出的結果會是 2010/4/19 下午 02:24:20

這些時間都不是我們要的,我們再看看 d2 的輸出結果:

  • 如果我們執行 d2.ToLocalTime() 輸出的結果會是 2010/4/20 上午 06:24:20 ( 正確答案 )
  • 如果我們執行 d2.ToUniversalTime() 輸出的結果會是 2010/4/19 下午 10:24:20

最後,我們再來看看時區的變化狀況:

  • 執行 .ToLocalTime() 會將目前的 DateTime 轉換為「本地時間」,當 d1 本身是 Local 的情況,本地時間當然與取得的時間一致,所以不會有變化。而 d2 本身已經宣告為 UTC 時間,所以當執行 .ToLocalTime() 時就會將目前的 DateTime 透過 本地時間UTC 時間 的差距(Offset)重新運算 ( +0800 ),並回傳在本機當時的時間點,而這個時間就是我們要的時間了。
  • 執行 .ToUniversalTime() 會將目前的 DateTime 轉換為「 UTC 時間」,當 d1 本身是 Local 的情況,在取得 UTC 時間時就會將目前的 DateTime 透過 本地時間UTC 時間 的差距(Offset)重新運算 ( -0800 ),所以得到的時間就是詭異的 2010/4/19 下午 02:24:20 時間,等於跟我們現在的本地時間差了 16 小時之多(當時就是這裡卡關的)。

對於 .NET 對於時間的知識建議可參考 MSDN 上 時間和時區 的說明,相信可以學習並認識更多對於時間的觀念,其中 在 DateTime、DateTimeOffset 和 TimeZoneInfo 之間選擇 對於時區轉換的觀念也有很大的幫助。

相關連結