Skip to content

필사 모드: 2026年依然没有闰秒 — 以及无人经历过的负闰秒

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

引言 — 闰秒,以及 UTC、TAI 与地球自转

大多数开发者写代码时,都假定时间"只是均匀地流逝"。但我们使用的 UTC,其实是两种不同时间之间的折中产物。一种是用原子钟测量的国际原子时(TAI),它吐出完美均匀的秒。另一种是由地球自转定义的天文时(UT1),而地球因为潮汐摩擦和内部扰动会微微晃动,自转速度也随之变化。

UTC 以 TAI 的均匀秒来滴答走时,但为了不让它和 UT1 相差超过 0.9 秒,会时不时插入 1 秒。这插入的 1 秒就是闰秒(leap second)。正闰秒就是这样产生的 — 那一天的最后一分钟变成 61 秒。

正闰秒:   23:59:58 → 23:59:59 → 23:59:60 → 00:00:00

问题的种子就埋在这里。23:59:60 这个时刻确实存在,但正如后文所示,Unix 时间里没有地方能装下它。自1972年引入以来,闰秒一共插入过27次,全都是正闰秒。最后一次是2016年12月31日。结果就是,现在 TAI - UTC = 37秒,反过来说就是 UTC - TAI = -37秒。

2026年:没有闰秒,以及史上第一次负闰秒

2026年7月6日,IERS 在巴黎发布了 Bulletin C 72。核心只有一行。

IERS Bulletin C 72 — Paris, 06 July 2026
"NO leap second will be introduced ... at the end of December 2026."
UTC - TAI = -37 s   (2017-01-01 以后无变动)

也就是说,2026年12月也没有闰秒(6月底也没有)。已经连续好几年没有任何调整了,但它之所以不是一条平静的新闻,是有原因的。大约从2016年开始,地球自转反而开始 加快 了。如果自转持续加快,让 UT1 跑到 UTC 前面,那么总有一天需要从 UTC 里减去 1 秒来追上它。这就是史上第一次需要插入负闰秒(negative leap second)的局面。

负闰秒和正闰秒正好相反,它抹掉 1 秒。那一天的最后一分钟变成 59 秒。

负闰秒:   23:59:58 → 00:00:00        (删除 23:59:59)

到目前为止的27次全都是正闰秒,负闰秒只存在于规范之中,在地球上任何地方都从未被真正应用过。BIPM 时间部门的 Patrizia Tavella 估计:"如果等到2035年,需要负闰秒的概率大约是30%。"另一方面,2022年 CGPM 第4号决议决定,在"2035年或更早"实际上废除闰秒,并把 UT1 与 UTC 之间的允许误差放宽得多;新的容许值预计将在2026年10月的第28届 CGPM 上确定。但这个决定也无法消除在2035年之前残留的风险。

为什么对分布式系统很危险 — POSIX 时间、NTP,以及 2012·2017 年的教训

Unix 时间的定义很简单:unix时间 = (纪元以来的天数) × 86400 + 当天的秒。也就是把"一天必然是 86400 秒"钉死了。所以没有地方能表示 23:59:60

  • 在正闰秒时,Unix 系统通常会重复那 1 秒,或者把时钟往回拨 1 秒。时间倒退,或者同一个时间戳出现两次(非单调)。
  • 在负闰秒时,时间戳会跳过一格。时间依然只向前走,但对应 23:59:59 那一格的 Unix 值,会成为一个不对应任何真实瞬间的"空洞"。

这个假设一旦被打破会发生什么,我们已经见识过了。2012年7月1日的闰秒中,Linux 内核定时器(hrtimer)的一个 bug 引发了活锁,让 CPU 飙到 100%,Reddit 和 Mozilla 等都受到了影响,Amadeus 航空订票系统的故障还导致 Qantas 的部分航班延误。大多数情况下,靠重启 NTP 或重置时钟渡过了难关。

2016年12月31日的闰秒在午夜生效时,Cloudflare 用 Go 写的 DNS 解析器假定"时间永远只向前走",在相减两个时刻时得到了负的时间间隔,这个值进入随机数范围的计算,导致部分 DNS 查询返回了错误。Cloudflare 公开了这份事后分析。

两起事件的根源是一样的 — 代码相信了"时间是均匀且单调地流逝的"。这里有一个反转。负闰秒是把时间向前跳,所以2017年 Cloudflare 那种"负间隔"的 bug 反而不会出现(单调性得以保持)。在 HN 的讨论里也有人指出,"向前跳并不会像向后跳那样制造出时间循环"。真正的问题在别处 — 负闰秒的处理路径,至今在任何生产系统中都从未被执行过。"一分钟是60秒"、只假设永远 +1 的硬编码闰秒表、把涂抹(smear)朝反方向(把时钟调快)运行的逻辑,全都是未经验证的代码。

NTP 这边也一样。Google、Amazon、Meta 会把闰秒在一段区间内"涂抹(smear)"掉(Google 和 AWS 是24小时)。所以应用程序根本看不到 23:59:60。但区间的长度和曲线各家公司都不一样,如果把做涂抹的服务器和不做涂抹的服务器混在同一个机群里,时钟就会彼此错开。在负闰秒时,涂抹需要把时钟稍微调快来回收 1 秒,而这同样从未大规模运行过。

结语 — 现在应该确认的事

  • 搞清楚你的平台是怎么处理闰秒的 — 是步进(step)、重复(repeat)还是涂抹(smear)。整个机群应该采用同一种方式,不要把做涂抹和不做涂抹的 NTP 混在一起。
  • 测量时长和超时,要用单调时钟(CLOCK_MONOTONIC),而不是挂钟(CLOCK_REALTIME)。2017年 Cloudflare 的 bug 正是挂钟相减。
  • 不要把"一分钟 = 60秒"、"一天 = 86400 秒"、"闰秒永远是 +1"写死进代码。
  • 不要把排序、唯一性、顺序保证寄托在挂钟时间戳上 — 用逻辑时钟、单调时间源,或 TrueTime 式的区间。
  • 去测试。只要让时钟真的跳过一格,就能模拟出负闰秒。大多数组织从来没做过这件事。

老实说,2026年12月什么都不会发生(没有闰秒)。这篇文章的重点不是制造恐惧,而是准备。地球自转已经掉头,总有一天(也许在2035年之前),我们会让全球同时踏上一条从未被执行过的代码路径。在那之前,最好先把那些盲目相信挂钟的代码挑出来。

参考资料

현재 단락 (1/30)

大多数开发者写代码时,都假定时间"只是均匀地流逝"。但我们使用的 UTC,其实是两种不同时间之间的折中产物。一种是用原子钟测量的国际原子时(TAI),它吐出完美均匀的秒。另一种是由地球自转定义的天文时...

작성 글자: 0원문 글자: 3,049작성 단락: 0/30