[桌邊服務] DateTime 本身有沒有包含時區的資訊?

  • 6288
  • 0
  • C#
  • 2019-04-15

雖然我們大部分的服務對象是在台灣,不過我們會揭露一些國際上的資訊給使用者知曉,關於時區的問題,沒有被砸過腳還真的不會去仔細研究,尤其某些時區還會實施日光節約時間,所以千萬不要再用加減小時法來處理時區轉換的問題,有興趣的朋友可以參考我過去的文章,今天要來討論的是 DateTime 本身有沒有包含時區的資訊?

先講結論,DateTime 本身是有時區資訊的,但不完整,DateTime 有一個型別為 DateTimeKind 列舉的 Kind 屬性,而 DateTimeKind 列舉只有三個值:DateTimeKind.LocalDateTimeKind.UnspecifiedDateTimeKind.Utc,意即 DateTime 本身所能表達的時區資訊只有三種:電腦所設定的本地時區、UTC、其他。

這個 Kind 屬性會影響時區轉換方法 ConvertTime(DateTime, TimeZoneInfo) 的執行結果,當 Kind 屬性值為 DateTimeKind.Local 時,則等同於將本地時間轉換成目標時區的時間,而 Kind 屬性值為 DateTimeKind.Utc 時,則等同於將 UTC 時間轉換為目標時區的時間,而當 Kind 屬性值為 DateTimeKind.Unspecified 時,則視為與 DateTimeKind.Local 情況相同。

由於 DateTime 的這種特性,會導致我們在做時區轉換時踩中一些誤區,舉例來說明好了。

誤區一

我們常會從外部收到表示為時間的字串,像是這樣 "2019-03-18 17:00:00",假設我們已知該時間字串為美國西岸的時間(Pacific Standard Time),我們想把它轉成美國東岸的時間(Eastern Standard Time)。

結果我們會發現 datetimeInEastern 原本預期應該是 "2019-03-18 20:00:00",卻變成 "2019-03-18 05:00:00",原因在於透過 DateTime.Parse(String) 轉換的時間,Kind 屬性值為 DateTimeKind.Unspecified,所以在轉換時區的時候,來源時區視為本地時區。

誤區二

我們有時候會在同一段程式碼處理多時區的資料,假設我們已經將本地目前時間(假定為 "2019-03-18 17:37:00")轉換成美國東岸的時間(Eastern Standard Time),轉出來是 "2019-03-18 05:37:00",這個沒問題。

接下來我想把轉好的美國東岸時間再轉成美國西岸時間(Pacific Standard Time),我們就會發現又錯了,原本預期 datetimeInPacific 的時間應該是 "2019-03-18 02:37:00",結果它變成 "2019-03-17 14:37:00",原因還是在於 datetimeInEastern 的 Kind 屬性值為 DateTimeKind.Unspecified。

上述這兩個誤區的原因都在於 DateTime 本身所能表示的時區資訊有限,所以我們在對 DateTime 做時區轉換時,最好使用 ConvertTime(DateTime, TimeZoneInfo, TimeZoneInfo) 方法,指定來源時區及目標時區。

除了指定來源時區及目標時區之外,還有另外一種可以避開誤區的方式,就是改用 DateTimeOffset,顧名思義,它比 DateTime 多了 Offset 資訊,用來表達時間的所屬時區,當誤區二的範例改用 DateTimeOffset 之後就完全沒毛病了。

以上,提供給各位朋友參考,還是再次提醒大家,不要再用加減小時的方式來轉換時區了。

相關資源

C# 指南
ASP.NET 教學
ASP.NET MVC 指引
Azure SQL Database 教學
SQL Server 教學
Xamarin.Forms 教學