闰秒串起了三种不同的时间观念
原子钟走得很稳,地球自转不稳。而民用时间想同时兼顾这两者。
闰秒是对协调世界时(UTC)做的一次一秒调整,目的是让它贴近 UT1——那个由地球自转推导出来的时标。这种调整很少见,会提前公告,而且对软件相当别扭:因为某一分钟的 UTC 里可能真的存在一个标着 23:59:60 的秒。
| 时标 | 由什么决定 | 连续吗 | 在应用中的角色 |
|---|---|---|---|
| TAI | 原子钟与 SI 秒 | 是 | 计量基准 |
| UT1 | 观测到的地球自转 | 步长不均匀 | 天文定向 |
| UTC | 原子速率加整秒调整 | 含有闰秒跳变 | 民用时间基准 |
| POSIX 时间 | 自 Unix 纪元起的名义秒数 | 定义上就不含闰秒 | 操作系统与应用 |
截至 2026 年 8 月,UTC 比 TAI 慢 37 秒,累计插入过 27 个正闰秒,而自 2016 年底以来一个都没再加过。IERS 的 Bulletin C 72 确认,2026 年 12 月末不会有闰秒。
UTC 为什么隔一阵子就得校一下
SI 秒是由原子物理定义的。而地球在和大气、海洋、地核以及自身其他部分交换角动量的过程中,自转速率会变;在更长的时间尺度上它同样会变。
UTC 按原子速率走,靠闰秒把 |UT1 − UTC| 控制在既定的 0.9 秒容差内。IERS 持续监测这个差值,大约每六个月发布一期 Bulletin C,宣布是否需要调整,或者确认这次不需要。
UTC 某一天末尾插入正闰秒时,是这样标记的:
23:59:58
23:59:59
23:59:60
00:00:00
机制上也允许负闰秒——直接跳过一个标号——但负闰秒从来没有出现过。
UTC、TAI、UT1 各回答一个不同的问题
给每个时标分配一份工作,偏移量就好理解多了:
- TAI 回答的是:连续的原子时间走到哪儿了。
- UT1 回答的是:地球此刻转到了什么朝向。
- UTC 提供的是:按原子速率走、同时在现行制度下贴近 UT1 的民用时间。
1972 年 UTC 采用现行安排时,它和 TAI 已经差了 10 秒。此后的 27 个正闰秒把差值推到了 37 秒。所以,把当前偏移说成“1972 年以来的 37 个闰秒”是不对的——它是 10 秒的初始差额,加上 27 个插入的秒。
POSIX 时间戳不会把每个闰秒都算进去
POSIX 用“每天 86,400 秒”的名义日和一个刻意忽略闰秒的公式来定义“自纪元以来的秒数”。这给了应用代码一个简单的整数模型,但也意味着:两个 POSIX 时间戳之差,在跨越闰秒插入时不一定等于实际流逝的 SI 秒数。
标准的定义并没有要求每个授时服务在操作层面用同样的方式处理这个边界。现实系统里用过的方案五花八门:
- 在闰秒附近重复或跳变某个时钟值
- 交由内核施加一次闰秒调整
- 把这一秒抹平摊到一个更长的窗口里
- 精密场景干脆改用 TAI 这类非 POSIX 时标
千万别光凭“我们用的是 NTP”这一句话就推断对方的闰秒行为。把时间源和时标明确写进文档里。
闰秒抹平:用一段时钟偏差换掉一次跳变
闰秒抹平(leap smear)会在闰秒前后把时钟速率微调一点,好让应用永远看不到 23:59:60,也看不到重复的一秒。时钟保持单调递增,但代价是它会暂时和严格意义上的 UTC 对不上。
Google 公布的推荐做法是从 UTC 正午到次日正午做 24 小时线性抹平。Amazon Time Sync Service 通过 NTP 分发抹平后的时间,但它的 PTP 硬件时钟走的是不抹平的 UTC。于是在闰秒事件期间,同一套系统里混用不同来源,就可能自己和自己对不上。
| 策略 | 好处 | 代价 |
|---|---|---|
| UTC 跳变或重复 | 忠实跟随公告的 UTC 事件 | 可能让定时器和依赖顺序的代码猝不及防 |
| 24 小时抹平 | 避免墙上时钟出现突兀跳变 | 期间与严格 UTC 存在偏差 |
| TAI 时钟 | 连续的 SI 秒时间轴 | 显示成 UTC 时需要显式换算 |
抹平没有一个所有厂商都遵循的统一标准。两个抹平时钟如果窗口或函数不一样,结果就会不一样。
应用到底是在哪儿受伤的
闰秒引发的故障,通常不是那个多出来的标号本身造成的,而是某个没人说破的隐含假设。
- 解析器拒绝
:60,可上游数据源偏偏会吐出它。 - 某段时长计算默认墙上时钟的时间戳必然严格递增。
- 两台主机用了不同的抹平策略,产出的时间戳在亚秒精度上根本没法比较。
- 数据库能接受普通的 RFC 3339 秒,却拒绝闰秒字符串。
- 应用把 POSIX、UTC、TAI、GPS、抹平时间混着用,却没记录用的是哪个时标。
- 监控把重复、跳过或放慢了的时钟读数当成脏数据处理。
对普通业务软件来说,最省事的答案通常是:老老实实用一个管理良好的授时服务,而不是自己去生成闰秒字符串。至于那些需要可追溯物理流逝时间的系统,应该采用一个明确定义的连续时标,并把换算所需的元数据维护好。
不用等下一次公告,现在就能测这个边界
用录制好的或构造的输入来测,别去动共享的生产时钟。
2016-12-31T23:59:59Z
2016-12-31T23:59:60Z
2017-01-01T00:00:00Z
至少覆盖这几种行为:
- 解析器碰到
:60秒是接受、拒绝,还是做了归一化? - 时钟发生重复或抹平时,排序还稳定吗?
- 数据模型能不能把时标和抹平策略记下来?
- 所有主机用的时间源是否彼此兼容?
- 测量耗时用的是单调时钟,还是墙上时钟?
- 万一真出现负闰秒(虽然从没发生过),系统扛得住吗?
“拒绝闰秒写法”完全可以是一个正当的约定。“以不可预测的方式挂掉”则不是。
连续 UTC 的方案还没最终敲定
第 27 届国际计量大会(CGPM)在 2022 年通过了第 4 号决议,要求在 2035 年或之前放宽 |UT1 − UTC| 的最大允许值。这样一来闰秒调整会变得极其罕见,UTC 得以在相当长的时间里连续运行。
这份决议本身并没有一劳永逸地敲定某个固定的 TAI-UTC 偏移,也没有把实现细节都规定清楚。为 2026 年第 28 届 CGPM 准备的一份草案提议让连续 UTC 于 2027 年 5 月 20 日生效,并把上限放宽到一小时。但在本文更新时那还只是草案,所以实现上应当以 BIPM 和 IERS 正式通过的决定为准,不要把提议中的日期当成板上钉钉的政策写进代码。
即便政策改了,历史数据、协议和测试用例里的闰秒行为依然存在。如果现在就把所有和闰秒相关的代码路径一刀切掉,那不过是拿一类 bug 换了另一类。
延伸阅读
Frequent questions:
- Q: 到目前为止一共有过多少个闰秒?
- A: 从 1972 年至今,UTC 一共插入了 27 个正闰秒。最近一次是在 2016 年 12 月 31 日末尾插入的,于 2017 年 1 月 1 日体现在 UTC-TAI 的偏移上。负闰秒一次都没有发生过。
- Q: 2026 年底会有闰秒吗?
- A: 不会。IERS 于 2026 年 7 月 6 日发布的 Bulletin C 72 明确说明,2026 年 12 月末不会引入闰秒。
- Q: UTC 和 TAI 有什么区别?
- A: TAI 是连续的原子时标。UTC 走的是同样的速率,但由于历次闰秒调整,目前和 TAI 相差一个整数秒。自 2017 年 1 月 1 日起,TAI 比 UTC 快 37 秒。
- Q: Unix 时间戳会把闰秒算进去吗?
- A: 按通常的定义,POSIX 时间不数闰秒。它把每一天都当作 86,400 个名义秒,所以时间戳并不等于自 1970 年以来物理上真实流逝的每一个 SI 秒。
- Q: 会出现负闰秒吗?
- A: 机制上允许,但从未发生过。负闰秒意味着从 UTC 里抹掉一秒,普遍被认为风险格外高——因为几乎没有系统真正经历过它。
- Q: 闰秒是要被废除了吗?
- A: 2022 年 CGPM 的决议要求在 2035 年或之前放宽允许的 UT1-UTC 差值上限,从而让 UTC 可以在很长时间内连续运行、不再频繁插闰秒。2026 年的一份草案提议把生效日期提前,但在正式通过之前它仍然只是草案。