日光節約時間說明:DST 如何影響 Unix 時間戳
日光節約時間在春季把本地時鐘前移、秋季後移。Unix 時間戳不受影響(它們始終計 UTC),但每一次 Unix 時間與本地時間之間的轉換都必須知道日光節約時間是否生效。本指南講解什麼是 DST、它在哪裡適用、它造成的空缺與重疊 bug,以及如何正確處理它。
一分鐘看懂 DST 與 Unix 時間
日光節約時間在春季把本地時鐘前移一小時、秋季後移一小時。Unix 時間戳從不移動——它計 UTC 秒並筆直地穿過每一次切換——所以它對 DST 免疫。改變的是你為它顯示的本地時間。兩次切換造成幾乎所有 DST bug:春季前移空缺(本地時間的一小時從不發生),以及秋季後移重疊(一小時發生兩次)。下表列出主要地區何時切換。
| 地區 | 春季切換(前移) | 秋季切換(後移) | 時鐘變化 |
|---|---|---|---|
| 美國與加拿大 | 3 月第二個星期日 | 11 月第一個星期日 | 02:00 → 03:00 本地 |
| 歐盟 | 3 月最後一個星期日 | 10 月最後一個星期日 | 01:00 UTC,+1 小時 |
| 英國 | 3 月最後一個星期日 | 10 月最後一個星期日 | GMT ↔ BST |
| 澳洲(實行的州) | 10 月第一個星期日 | 4 月第一個星期日 | 南半球 —— 相反 |
什麼是日光節約時間?
日光節約時間(DST)是在一年中的一段時間把本地時鐘前移一小時、以延長傍晚日照的做法。目前約 70 個國家實行,多在溫帶緯度。切換是掛鐘時間的不連續,而非 UTC 的不連續——Unix 時間戳穿過每一次前移和後移都平滑地繼續走動。
- 北半球:時鐘通常在 3 月前移、在 10 月/11 月後移
- 南半球:時鐘在 10 月前移、在 3 月/4 月後移
- 熱帶和赤道地區一般不實行 DST
- DST 給傍晚增加一小時日照,但不改變日照的總量
DST 切換如何影響 Unix 時間戳
Unix 時間戳是自 1970 年 UTC 以來流逝的秒數,所以它不能向前跳或重複。改變的是這些秒的本地時間解讀。在春季前移切換時,本地時鐘從 02:00 跳到 03:00——02:00 到 02:59 的掛鐘時間在本地從不出現。在後移時,本地時鐘的 01:00 到 01:59 出現兩次,在 UTC 中相隔一小時。
- 任何 Unix 時間戳都不會被 DST 跳過或重複
- 一個給定的 Unix 時間戳對應一個具體的本地時間,含偏移(PDT vs PST、BST vs GMT 等)
- 一個給定的本地時間可能對應零個(DST 空缺)或兩個(DST 重疊)Unix 時間戳
- 使用 IANA 名稱(America/Los_Angeles)讓 DST 自動為你套用;若該地實行 DST,絕不要寫死 UTC−8 這樣的偏移
DST 空缺與 DST 重疊詳解
兩次切換造成兩個經典 DST bug。春季前移空缺是排程和提醒失敗最常見的根源——在切換日設於 02:30 的事件在本地不存在。秋季後移重疊更隱蔽——切換日 01:30 的事件存在兩次,相隔一小時。兩種情況都需要一個明確的策略。
| 切換 | 時鐘動作 | 對本地時間的影響 |
|---|---|---|
| 春季前移 | 02:00 → 03:00 | 02:00–02:59 從不存在(「空缺」) |
| 秋季後移 | 02:00 → 01:00 | 01:00–01:59 出現兩次(「重疊」) |
- 空缺:ISO 8601 / RFC 3339 無法無歧義地表示空缺之內的時間
- 重疊:用明確偏移(UTC−5 vs UTC−4)標註是哪一次出現
- Python:zoneinfo 透過 fold 屬性處理空缺/重疊(0 = 第一次出現,1 = 第二次)
- JavaScript Temporal:ZonedDateTime 接受一個消歧選項("earlier"、"later"、"compatible"、"reject")
- DST 地區 02:30 的 cron 在前移時跳過、在後移時觸發兩次——盡量按 UTC 排程
哪些地區實行 DST
DST 的採用參差不齊且經常變化——務必查閱目前的 IANA tzdata,而不要寫死任何規則。以下對 2026 年大體準確。
- 美國:多數州實行 DST(3—11 月)。亞利桑那(納瓦荷族保留地除外)和夏威夷不實行
- 加拿大:多數省實行 DST。薩斯喀徹溫、育空大部分,以及(自 2026 年起)不列顛哥倫比亞大部分不實行
- 歐盟:實行 DST(3 月最後一個星期日 → 10 月最後一個星期日)。2019 年廢除 DST 的投票已被推遲
- 英國:夏季以 BST(UTC+1)實行 DST
- 墨西哥:2022 年廢除全國 DST;只有與美國對齊的邊境市鎮仍實行
- 澳洲:僅 NSW、維多利亞、塔斯馬尼亞、ACT 和南澳實行 DST(10—4 月)
- 亞洲、非洲和南美洲大部分:沒有 DST
在程式碼中安全處理 DST
可靠的做法是把一切錨定在 Unix 時間戳(或帶 IANA 時區的日期時間)上,並把本地時間當作呈現層的事情。
- 儲存:Unix epoch 秒,或一個帶時區的 datetime(例如 Postgres 的 TIMESTAMPTZ)
- 計算:在 UTC,或在支援 IANA 的日期時間函式庫(Temporal、zoneinfo、java.time)中
- 顯示:在呈現時轉換為使用者的 IANA 時區
- 排程:對應保持在「本地上午 9 點」的重複事件,把 IANA 時區與本地掛鐘一起儲存
- 絕不要把縮寫 EDT/PDT/CET 當作 IANA 名稱的替代來儲存
廢除 DST 的推動
多個立法機構已投票廢除一年兩次的時鐘變更;其中很少有投票真正帶來政策改變。近期值得注意的動向:
- 2022 年:美國參議院一致通過《陽光保護法》,在眾議院擱淺
- 2022 年:墨西哥廢除全國 DST
- 2024 年:川普公開反對永久 DST;參議院勢頭放緩
- 2026 年:不列顛哥倫比亞成為永久 UTC−7;美國眾議院能源與商業委員會推進了新的 DST 廢除條文
- 2019 年:歐盟原則上投票廢除 DST,但一再推遲實施
相關參考
FAQ
- DST 會影響 Unix 時間戳嗎?
- 不會。Unix 時間戳計自 1970 年起的 UTC 秒,並平滑地穿過每一次 DST 切換。改變的只是這些秒的本地時間解讀。
- 什麼是含糊時間?
- 含糊時間是一個對應兩個不同 UTC 時刻的本地時間。它發生在秋季切換,當時鐘從 02:00 退回 01:00,而 01:00–01:59 這一小時出現兩次。用明確偏移或某個函式庫的 fold/消歧選項來使其唯一。
- DST 切換時排程工作會怎樣?
- 在 DST 地區設於 02:30 的 cron 工作,會被跳過(春季前移)或執行兩次(秋季後移)。修正辦法是按 UTC 排程,或使用一個明確消歧的排程器。
- 程式碼裡該用 "EST" 還是 "America/New_York"?
- America/New_York。IANA 名稱會在冬季自動套用 EST(UTC−5)、夏季套用 EDT(UTC−4)。寫死 \"EST\" 意味著值有半年是錯的。
- 如何在程式碼中正確處理 DST?
- 以 UTC(或帶 IANA 時區的日期時間)儲存和計算,僅在顯示時轉換為本地時間。使用 America/New_York 這樣的 IANA 時區名,絕不要用固定偏移或 EST 這類縮寫,這樣 DST 會自動為你套用。
- 2026 年 DST 何時開始與結束?
- 在美國,DST 於 2026 年 3 月 8 日本地 02:00 開始(前移到 03:00),於 2026 年 11 月 1 日本地 02:00 結束(後移到 01:00)。在歐盟,切換在 2026 年 3 月 29 日和 10 月 25 日。