【黑暗執行緒】【茶包射手筆記】重新認識 DateTime.Parse() 時區問題

2022/6/2

收到友人貢獻茶包一枚。

.NET DateTime.Parse() 有其好用之處,可以解析各種日期時間格式:

	Console.WriteLine(DateTime.Parse("2022-06-01"));
	Console.WriteLine(DateTime.Parse("2022/06/01"));
	Console.WriteLine(DateTime.Parse("06/2022"));
	Console.WriteLine(DateTime.Parse("2022/06/01 21:00"));
	Console.WriteLine(DateTime.Parse("6/1/2022 9:00:00 PM"));
	Console.WriteLine(DateTime.Parse("2022/6/1 下午 09:00:00"));
	Console.WriteLine(DateTime.Parse("Wed, 1 Jun 2022 13:00:00 GMT"));

之前有研究過 DateTime.Parse(),甚至踩過數字 3.1416 被 DateTime.Parse() 解析成 1416-03-01 的雷,對於輸入格式較固定的情境,我偏好 DateTime.ParseExact(),

今天的案例讓我復習到 DateTime.Parse() 決定時區的規則

一般來說,DateTime.Parse 傳回 DateTime 物件的 Kind 屬性為 DateTimeKind.Unspecified,但在以下情況則會轉為 Local 或 Utc:

  1. 日期時間字串包含時區資訊,例如:Z 或 +08:00 結尾、GMT 字樣
    轉換為本地時間,Kind = DateTimeKind.Local
  2. 日期時間字串包含時區資訊,DateTimeStyles 參數包含 AdjustToUniversal
    轉換成 UTC 時間,Kind = DateTimeKind.Utc
  3. 字串包含 Z 或 GMT,且 DateTimeStyles 參數包含 RoundtripKind
    解析為 UTC 時間,Kind = DateTimeKind.Utc

實測驗證如下:


回到茶包案例:程式用 DateTime.Parse() 解析外部傳入的 yyyy-MM-dd 字串轉為 DateTime 存入資料庫。資料來源意外改變了日期格式,在 yyyy-MM-dd 之外加上 +00:00 時區,由 2022-06-01 改為 2022-06-01+00:00,依上述規則,解析出來的 DateTime 原本是 Unspecified 2022-06-01 00:00:00,現在變成 Local 2022-06-01 08:00:00,程式沒出錯但時間差了八小時,費了點功夫追查才找出原因。

如果用 DateTime.ParseExact() 可以避免嗎?

如上所示,ParseExact 只要格式改變,解析時便會直接噴錯,遠比錯誤資料流入後端程序容易偵測及善後。因此,我認為對固定格式來源使用 ParseExact() 是較好的設計。

本文分享自 黑暗執行緒