Daylight Saving Time

日光節約時間說明: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:0002:00–02:59 從不存在(「空缺」)
秋季後移02:00 → 01:0001: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 日。