约 4 分钟阅读更新于

Unix 时间戳完全指南:闰秒、2038 年与夏令时陷阱

作者:Safe Local Tools 编辑组

Unix 时间戳看起来只是一个整数,背后却叠着三套现实:民用历法、政治时区,以及硬件字长。 在全球排障时把它当作「UTC 瞬间」来想;但一旦落到本地展示,就要记住许多地区每年会因夏令时把墙钟拨快或拨慢。

工程师喜欢用「自 1970-01-01T00:00:00Z 起的秒数」存事件,因为比较与做差都很直接——直到本地化、闰秒、历史偏移,或 32 位有符号整数溢出 出现。本文梳理闰秒与 POSIX 的常见取舍、2038 问题在各语言里的表现、时区陷阱如何悄悄改写字段,并说明 Safe Local Tools 在浏览器本地做时间戳转换,适合在不能把内部事件 ID 贴到陌生 SaaS 的场景下调试。

OG illustration

大多数开发者口中的 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」

常见事故包括:

  1. 把本地无时区字符串当 UTC 解析(CSV 导出最爱干这事);
  2. 夏令时切换日 的「不存在的一小时」与「重复的一小时」;
  3. 混用秒与毫秒 导致排序颠倒,却在图表上看起来像「随机乱序」。

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 时间戳转换工具 →