Skip to main content

Swift DateFormatter 無法解析日期

·261 words·2 mins·
Dst Daylight Saving
Author
Andreas Kung

同事遇到, DateFormatter 遇到某些特定日期時, parse 出來會是 nil,一開始遇到的日期是, “1974/4/1”。

因為他要做的是計算使用者生日 ~ 今天 是否已經滿 18 歲。

let df = DateFormatter()
df.dateFormat = "yyyy/MM/dd"
let date1974 = df.date(from: "1974/4/1")

一開始看到程式碼覺得很怪, 要算幾天應該是用像是 Java 的 LocalDate, 不分時區, 只有日期才對, 可惜 Swift 這麼棒的語言沒有 LocalDate 這樣的 class, 也沒有 joda time。

DateFormatter 的文件有說到

When working with fixed format dates, such as RFC 3339, you set the dateFormat property to specify a format string. For most fixed formats, you should also set the locale property to a POSIX locale ("en_US_POSIX"), and set the timeZone property to UTC.

對大多數的 fixed formats? 應該要設定 locale POSIX 跟 timeZone UTC。

let df = DateFormatter()
df.dateFormat = "yyyy/MM/dd"
df.timeZone = TimeZone(secondsFromGMT: 0)

let date1974 = df.date(from: "1974/4/1")
//Optional(1974-04-01 00:00:00 +0000)

這樣結果就會是正確的 1974-04-01 了。

時區 UTC+8, Asia/Taipei ?
#

let dfTz8 = DateFormatter()
dfTz8.timeZone = TimeZone(identifier: "UTC+8")
print(dfTz8.date(from:"1974/4/1"))
//1974-03-31 16:00:00 +0000

其實把時區設成 UTC+8 也可以成功解析 1974/4/1 這個日期, 只是會變成 1974-03-31 16:00:00 +0000 (不是 4/1 喔)。 那 DateFormatter 一開始的時區是什麼?

let df = DateFormatter()
print(df.timeZone.description)
print(TimeZone(identifier: "UTC+8")?.description)

結果一個是 Asia/Taipei 一個是 GMT+0800

這兩個有什麼不同?

GMT+8 就是固定的, 不管是 1900 年還是 2000 年都是固定 +8。 Asia/Taipei, Asia/Singapore 等等時區雖然目前都是 +8, 但是會有各自國家的歷史, 可能在過去某時候新加坡就用過 +7 的時間(1905 ~ 1935), 或是有 20, 30 分鐘的 DST offset (1936 ~ 1941, 1941 九月到 1942 二月 +30), 被日本佔領時候又改成跟日本用相同 +9 時區。

Singapore Time

所以文件才會說要設置 timezone utc+0。

查看時區有沒有 DST
#

TimeZone 沒有 api 可以看時區所有歷史有沒有發生過日光節約, 但是可以查看某時間之後下一個會發生日光節約的時間。

import UIKit

let df = DateFormatter()
df.dateFormat = "yyyy/MM/dd"
let date1900 = df.date(from: "1900/01/01")!

let tz8 = TimeZone(identifier: "UTC+8")
let tzTpe = TimeZone(identifier: "Asia/Taipei")


print(tz8?.nextDaylightSavingTimeTransition(after: date1900))

print(tzTpe?.nextDaylightSavingTimeTransition(after: date1900))

1900/1/1 之後, UTC+8 時區 沒有發生任何日光節約, 但是 Asia/Taipei 在 1937-09-30 有發生過(或者是說發生時區改變, 從大日本帝國 西部標準時+9 改成 中央標準時+8)。

Wiki 台灣時區

台灣時區變換的八卦

結論
#

只是想要不管時區比較一個日期, 為什麼 Swift 一定要給我牽扯到時區, 真麻煩。


comments powered by Disqus