Unix 时间戳完全指南:闰秒、2038 年与夏令时陷阱
作者:Safe Local Tools 编辑组
Unix 时间戳看起来只是一个整数,背后却叠着三套现实:民用历法、政治时区,以及硬件字长。 在全球排障时把它当作「UTC 瞬间」来想;但一旦落到本地展示,就要记住许多地区每年会因夏令时把墙钟拨快或拨慢。
工程师喜欢用「自 1970-01-01T00:00:00Z 起的秒数」存事件,因为比较与做差都很直接——直到本地化、闰秒、历史偏移,或 32 位有符号整数溢出 出现。本文梳理闰秒与 POSIX 的常见取舍、2038 问题在各语言里的表现、时区陷阱如何悄悄改写字段,并说明 Safe Local Tools 在浏览器本地做时间戳转换,适合在不能把内部事件 ID 贴到陌生 SaaS 的场景下调试。

大多数开发者口中的 Unix 时间
日常所说的 Unix time,通常指 UTC 下、不计闰秒 的秒计数(具体实现以你所用库为准)。展示给人看时,会借助 IANA 时区数据库解释 DST 规则。背景可参考 维基百科:Unix 时间。
闰秒:天文日、UTC 与软件计时器
地球自转并不恒定,UTC 偶尔需要 闰秒 与天文日对齐。不同系统对闰秒的处理并不一致:有的「涂抹」、有的重复一秒、有的直接忽略——POSIX 生态历史上就存在实现差异。
运维上要分清两件事:
- 墙上时钟(人类读的本地时间)可能跳变;
- 单调时钟(
CLOCK_MONOTONIC一类)适合测耗时,不应与 epoch 混谈。
排障日志时,如果只看「本地格式化字符串」而不看 epoch,很容易在闰秒附近得出看似矛盾的时间线。
2038 年问题:32 位仍藏在角落里
time_t 在许多平台上曾是 32 位有符号整数,2038-01-19 03:14:07 UTC 之后会溢出。现代 64 位服务器大多已缓解,但 嵌入式固件、旧数据库列、遗留 API 仍可能以秒级 INT 存储。
实践建议:
- 新系统优先 毫秒级
BIGINT或 64 位 epoch; - 字段名带单位后缀:
created_at_epoch_ms,禁止含糊的time; - 在边界日期附近做回归测试,而不是只靠「现在离 2038 还远」。
时区陷阱:DST、历史偏移与「看似 UTC」
常见事故包括:
- 把本地无时区字符串当 UTC 解析(CSV 导出最爱干这事);
- 夏令时切换日 的「不存在的一小时」与「重复的一小时」;
- 混用秒与毫秒 导致排序颠倒,却在图表上看起来像「随机乱序」。
MDN:Date 文档强调:JS 的 Date 内部是 UTC 毫秒,但许多格式化 API 会按本地时区解释——这正是混乱来源。
// 明确:API 返回的是秒还是毫秒?
const epochSeconds = 1715600000;
const asMs = epochSeconds * 1000;
console.log(new Date(asMs).toISOString());JWT、日志与跨系统关联
JWT 的 exp/iat 通常是 NumericDate(秒);而前端性能埋点、部分云厂商 SDK 更爱 毫秒。做 join 前先在契约层统一单位,否则 on-call 会在「事件 A 居然排在 B 后面」上浪费一小时。
属性测试:DST 边界值得写一次
用权威时区库生成 DST 切换附近 的随机瞬间,断言 epoch ↔ ISO 字符串往返稳定;并写明区间是 [start, end) 还是闭区间。营销文案里的「截止到周五结束」与工程语义经常不一致。
Safe Local Tools:本地转换,减少泄露面
事故响应时常要把日志里的 epoch 整数翻译成可读时刻;把内部 ID 上传到随机在线转换站,会无谓扩大披露面。
Safe Local Tools 在浏览器内完成时间戳转换,适合敏感排障流程——仍建议与监控系统里的权威时间对照。
上线前 checklist:
- 清点仍使用 32 位 epoch 的列;
- API 统一 秒或毫秒 并在字段名体现;
- 审计链路同时落 UTC ISO 与数值 epoch,便于对账。
当你需要并排查看 trace 里的 epoch 与 JWT exp 时,试用 Unix 时间戳转换工具 →