导读:本期聚焦于小伙伴创作的《系统时间遇到闰秒Leap Second到底该怎么处理才安全》,敬请观看详情。闰秒会在UTC中插入或删除一秒,导致系统时间出现23:59:60这样的特殊时刻。若直接依赖内核步进,可能引发程序时间回退、数据库事务乱序。常见误区是认为NTP会自动无缝修正,其实ntpd与chrony行为不同。正确做法是在应用层做时间单调性保护,或用clock_discipline将闰秒摊薄到较长时间。本文从原理、系统配置、代码兼容三方面说明如何避免闰秒故障。

闰秒是国际标准时间UTC为了对齐地球自转与原子钟而产生的一秒调整,分为正闰秒与负闰秒。当正闰秒发生时,当天的23:59:59之后会插入23:59:60,再进入次日00:00:00;负闰秒则会直接跳过23:59:59。对大多数依赖单调时间的服务而言,这种非 monotonic 的跳变会破坏计时假设,造成证书校验失败、日志错乱、分布式锁超时异常。理解操作系统与NTP实现如何应对闰秒,是构建稳定系统的前提。

系统时间遇到闰秒Leap Second到底该怎么处理才安全

闰秒的底层传播机制与内核行为

闰秒信息并不是凭空出现在操作系统里的,它通常由上级时间源通过NTP或PTP协议下发。NTP报文中的闰秒标志位会在闰秒来临前的一段时间被设置,操作系统内核据此决定将这一秒体现为时间回退还是平滑 smear。Linux内核自2.6起支持通过adjtimex系统调用读取状态,当状态为TIME_INSTIME_DEL时,说明内核已知晓即将发生的闰秒。

传统内核在默认配置下会采用 stepping 方式,即在闰秒瞬间让gettimeofday返回包含60秒的时刻,随后立刻跳到次日零时。这种方式对认为时间是严格递增的程序是致命的,因为从60秒跳到00秒相当于时间倒流了一秒。某些文件系统与数据库在检测到时间回退时会拒绝写入,防止数据覆盖。因此,理解内核状态机比盲目升级NTP更重要。

另一种做法是让NTP守护进程在用户态将闰秒摊薄,也就是所谓的 leap smear。此时内核始终认为时间是连续递增的,闰秒被均摊到数小时或一整天中,每秒微调几微秒。这种方案对应用完全透明,但要求全网设备采用相同的 smear 策略,否则跨机器时间对比会偏差明显。

ntpd与chrony的闰秒配置差异

使用广泛的ntpd默认并不做 smear,它依赖内核完成闰秒插入,因此管理员常会观察到23:59:60这一特殊秒。若业务无法容忍该跳变,可以改用chrony,它在配置文件中加入leapsecmode smeared后,会把闰秒均摊到约24小时。下面是一段典型的 chrony 配置示例,展示了如何开启平滑模式。

# /etc/chrony.conf 示例
server time.ipipp.com iburst
leapsecmode smeared
maxdistance 3.0
rtcsync

对比之下,ntpd如果需要类似能力,必须借助外部脚本在闰秒前调用adjtimex修改时钟斜率,或者切换到支持 smeared 时间的上级服务器。很多团队在混合环境中同时运行两种服务,结果因策略不一致造成节点间时间差超过容忍阈值。因此,统一时间中间件策略比单纯追求精度更关键。

还需要注意,当系统使用虚拟化平台时,宿主机和虚拟机的闰秒处理可能脱节。如果宿主机内核直接 step,而虚拟机内的 chrony 在做 smear,虚拟机会出现相对宿主机慢一秒的错觉。在金融交易系统中,这类偏差足以触发风控拦截,所以应在虚拟化层明确禁用宿主机的 step 行为。

应用层如何写出闰秒安全的代码

无论底层如何配置,应用层都不应假设System.currentTimeMillistime.time永远递增。最稳妥的做法是使用单调时钟接口,例如 Linux 的clock_gettime(CLOCK_MONOTONIC),它不受闰秒与手工校时影响。下面的 C 示例演示了如何获取单调递增时间,用于计算间隔。

#include <time.h>
#include <stdio.h>

int main() {
    struct timespec ts;
    // 使用单调时钟,闰秒不会让其回退
    clock_gettime(CLOCK_MONOTONIC, &ts);
    printf("sec=%ld nsec=%ldn", ts.tv_sec, ts.tv_nsec);
    return 0;
}

在 Java 中,应避免用new Date()做耗时统计,而使用System.nanoTime。Python 则推荐time.monotonic。这些接口在闰秒发生时依然保持向前,适合生成事务序号、限流计数。若业务逻辑必须使用墙上时钟,则应捕获时间回退事件,记录告警而非直接崩溃。

对于必须处理日期展示的场景,例如日志系统,应当兼容23:59:60的解析。许多旧版时间库会将该字符串判为非法,导致解析异常。可以用正则先放通60秒,再在存储层转成 TAI 偏移。如下 Python 片段展示了宽松解析思路。

import re
from datetime import datetime

def parse_lenient(s):
    # 允许秒为60
    m = re.match(r'(d{4})-(d{2})-(d{2})T(d{2}):(d{2}):(6d)', s)
    if m and m.group(6) == '60':
        base = s[:17] + '59'
        return datetime.fromisoformat(base)
    return datetime.fromisoformat(s)

print(parse_lenient('2023-06-30T23:59:60'))

最后,分布式系统应在设计协议时携带逻辑时钟或版本号,而不是单纯依赖物理时间排序。当闰秒导致个别节点慢一秒,只要逻辑时钟比较机制健全,就不会出现乱序提交。将时间处理从核心链路剥离,放到边缘适配层,是大型系统抵御闰秒风险的有效架构思路。

Leap_SecondNTPclock_discipline修改时间:2026-08-15 08:54:13

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。