一句话说明
Unix 时间戳统计自约定纪元起经过的秒数(有时是毫秒):1970-01-01T00:00:00Z——1970 年 1 月 1 日 UTC 午夜——在常见 POSIX 定义中不含闰秒。这一个整数即可无歧义地存储“某一瞬间”,无需附带时区标签。
可用 Unix 时间戳转日期 将数值戳转为可读日期。
工程师为何偏好纪元秒
民用日期依赖日历、时区与夏令时规则。瞬时事件——“订单已支付”“证书过期”“日志行已写入”——更适合用从固定原点起的单调计数来存。Unix 时间在语言、数据库与 API 之间迁移方便:排序就是整数排序,差值就是减法。
代价是人读性:1710000000 在转换前几乎没有意义。工具与库的格式化函数填补这一缺口。
秒、毫秒与纳秒
惯例不同:
| 单位 | 典型用途 | 示例位数(2020 年代) |
|---|---|---|
| 秒 | POSIX、许多 API | 10 位 |
| 毫秒 | JavaScript Date、部分数据库 | 13 位 |
| 微/纳秒 | 高分辨率指标 | 更长的整数 |
把毫秒值传给期望秒的 API 会得到遥远未来的日期;把秒传给毫秒 API 会落在 1970 附近。集成时查文档,并用已知瞬间做测试。
JavaScript 的 Date.now() 返回毫秒。许多后端框架默认用秒。JSON 字段名尽量写清单位(如 createdAtSeconds)。
UTC 与本地显示
时间戳本身以 UTC 为基准。在柏林或东京显示时,只是为人类做时区换算;存储的整数不变。请以 UTC 瞬间存储,在边缘(界面、邮件、报表)再本地化。
负时间戳与遥远未来
1970 年之前的时间戳在支持的系统上为负。过远的未来可能溢出 32 位有符号整数:这是 32 位平台上 time_t 的经典 2038 年问题。现代 64 位 time_t 在秒级分辨率下把溢出推到很远。新系统应优先使用 64 位类型。
闰秒(需要知道的程度)
UTC 偶尔插入闰秒。POSIX Unix 时间通常忽略闰秒,把每天当作 86 400 秒。日常业务应用够用。天文、电信或跨越闰秒事件、需秒级精度的法律时间戳,应研究行业时间尺度(UTC、TAI、GPS 时间)与库支持。
ISO 8601 与 Unix 时间并存
API 常同时接受:
17100000002024-03-09T16:00:00Z
带 Z 或数字偏移的 ISO 8601 字符串在正确解析时表示同一瞬间。Unix 时间紧凑;ISO 字符串便于在日志中搜索。许多系统存整数,在 JSON 中输出 ISO。
转换流程
- 确认单位(秒/毫秒)。
- 将数字解释为 UTC 纪元。
- 用明确时区格式化供显示。
- 往返校验:格式化再解析,或转回纪元,确认一致。
不用库的手算在月份长度与闰年附近易错——生产环境请用经过测试的库。
数据库与索引
存储纪元整数或归一化为 UTC 的原生时间戳类型。若含义是瞬间,避免存无时区的本地民用时间。整数纪元索引简单;在 schema 注释中写明单位。
对发票“日历日”这类业务日期,无时间的 date 类型可能优于午夜纪元——“一天”的含义取决于政策与法域。
日志与可观测性
日志处理器偏爱 Unix 时间。跨服务关联追踪时尽量使用同一时钟域。主机间仍有时钟偏差——NTP 有帮助——时长计算中容忍小负值,审计轨迹优先用服务器生成的时间戳。
常见错误
- 单位混用(秒与毫秒)。
- 把纪元当本地时间,格式化时缺少
Z。 - 对日期字符串做运算而不是对瞬间运算。
- Excel 用错误纪元转换时间戳(Excel 的“零日”不同)。
- 32 位溢出(嵌入式或遗留系统)。
教学示例
取一个时间戳,转为 UTC,再转为两个民用时区。确认两条民用时间字符串对应同一瞬间。这比背公式更能建立直觉。
可用 from-unix-timestamp-to-date 检查 API 或数据库导出中的值(使用非敏感样本)。
相关概念
- 单调时钟测量已经过时间,不应回退;它们不是 Unix 时间戳,也不应作为挂钟时间持久化。
- Cron 表达式描述民用日程,不是纪元。
- 证书
notAfter字段是瞬间——在 UTC 下比较。
小结
Unix 时间戳把瞬间编码为自 1970-01-01 UTC 起的计数。它们简化存储与排序,把人类可读格式推到边缘。弄清单位、使用 64 位类型、用明确时区显示,并在需要把原始数字读成日历日期时用可靠工具转换。