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 日。