3

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

 1 year ago
source link: https://blog.darkthread.net/blog/datetime-parse-timezone-issue/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

收到友人貢獻茶包一枚。

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

Fig1_637896916733166496.png
	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

實測驗證如下:

Fig2_637896916734230904.png

void Main()
{
	test("2022-06-01");
	test("2022-06-01 00:00");
	Console.WriteLine("\n日期時間字串包含時區資訊");
	test("2022-06-01+08:00");
	test("2022-06-01+00:00");
	test("2022-06-01T00:00:00Z");
	test("2022-06-01Z");
	Console.WriteLine("\n日期時間字串包含時區資訊 + AdjustToUniversal");
	test("2022-06-01Z", DateTimeStyles.AdjustToUniversal);
	test("2022-06-01+08:00", DateTimeStyles.AdjustToUniversal);
	test("2022-06-01+08:00", DateTimeStyles.AdjustToUniversal);
	//不合條件(無時區資訊)
	test("2022-06-01", DateTimeStyles.AdjustToUniversal);
	Console.WriteLine("\n日期時間字串包含Z、GMT + RoundtripKind");
	test("2022-06-01 Z", DateTimeStyles.RoundtripKind);
	test("2022-06-01 GMT", DateTimeStyles.RoundtripKind);
	//不合條件(非 Z 或 GMT)
	test("2022-06-01+08:00", DateTimeStyles.RoundtripKind);
	test("2022-06-01+00:00", DateTimeStyles.RoundtripKind);
}
void test(string t, DateTimeStyles s = DateTimeStyles.None)
{
	var d = DateTime.Parse(t, null, s);
	Console.WriteLine(d.Kind + " | " + d.ToString());
}

回到茶包案例:程式用 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() 可以避免嗎?

Fig3_637896916734717153.png

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

and has 2 comments

Comments

# 2022-06-02 09:43 AM by 赫達利 協理 JOJO徐

黑暗大大,您好:

在下時常拜讀您的文章, 有許多文章都協助在下處理掉難解的問題 為了希望之後本公司其他工程師也可以方便地找到您的文章增進知識 並能增加本公司網站的活絡度 因此在下想要將您的文章分享在在下任職的公司官網當知識分享文章 並統一會使用您的Logo與文末連結宣告出處為此部落格,以宣告文章作者為您 且保證不會用於商業用途 請問能否同意本公司分享您的文章?? 感謝您的回應

本公司的網址 https://www.gomoney.com.tw/ Email:[email protected]

Post a comment

Comment
Name Captcha 53 - 37 =

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK