Unix 时间戳完全指南:从纪元到 2038 问题
2026-08-06
打开任何一个后端日志、数据库表或者 API 响应,你几乎都能看到一串像 1754467200 这样的数字。它就是 Unix 时间戳——计算机世界表示时间的通用语言。这串数字看起来简单,实际用起来却处处是坑:多三位少三位结果差几千年、时区转换莫名其妙差八小时、老系统在 2038 年会集体”穿越”。这篇文章把 Unix 时间戳的来龙去脉和最常见的坑一次讲清楚。
什么是 Unix 时间戳
Unix 时间戳的定义只有一句话:从 1970 年 1 月 1 日 00:00:00 UTC 起经过的秒数。这个起点叫做 Unix 纪元(Unix Epoch)。比如时间戳 0 就是纪元时刻本身,1754467200 对应 2026 年 8 月 6 日的某个时刻。
为什么是 1970 年?这并不是什么深思熟虑的决定,而是一个历史巧合。Unix 系统在贝尔实验室诞生时大约是 1969 到 1971 年,早期版本甚至以 1971 年为起点、以 1/60 秒为单位计时。后来工程师把起点调整为 1970 年 1 月 1 日、单位改为秒——一个”足够近、又是整数”的约定。这个约定随着 Unix 和 C 语言的传播成为事实标准,如今几乎所有编程语言、数据库和网络协议都内置了对它的支持。顺便一提,1970 年 1 月 1 日是星期四,这个冷知识在验证日期计算代码时偶尔有用。
第一大坑:秒与毫秒的混淆
Unix 时间戳的经典定义是秒,但 JavaScript 的 Date.now()、Java 的 System.currentTimeMillis() 返回的是毫秒。这一字之差是实际开发中最常见的时间 bug 来源。分辨方法很简单:
秒级时间戳: 1754467200 (10 位数字)
毫秒时间戳: 1754467200000 (13 位数字)
如果把毫秒当秒解析,1754467200000 秒对应的是公元 57600 多年——程序不会报错,只会安静地给你一个荒谬的日期。反过来把秒当毫秒解析,你会得到一个 1970 年 1 月的时刻,一切”最近创建的数据”都显示为五十多年前。排查这类问题时,先把数字粘贴到 时间戳转换工具 里,切换秒/毫秒单位看看哪个结果落在合理的日期范围,十秒钟就能定位问题。
各语言获取当前时间戳的写法也不一样,注意单位:
import time
int(time.time()) # 秒
int(time.time() * 1000) # 毫秒
Math.floor(Date.now() / 1000) // 秒
Date.now() // 毫秒
时间戳与时区无关
另一个常见误解是”时间戳要带时区”。实际上 Unix 时间戳本身没有时区概念——它永远相对于 UTC 纪元计数,同一时刻全世界的时间戳完全相同。北京的用户和纽约的用户在同一秒调用接口,得到的时间戳一模一样。
时区只在显示环节才参与进来:把时间戳格式化成人类可读的字符串时,系统按照本地时区换算。同一个 1754467200,在 UTC+8 显示为 2026-08-06 16:00:00,在 UTC-4 显示为 2026-08-06 04:00:00——它们是同一个瞬间。这带来一条重要的工程原则:存储和传输一律用时间戳或 UTC 时间,时区转换只在最后展示给用户时做一次。如果需要确认不同时区的对应时刻,可以用 世界时钟 直观地对比多个城市的时间,或者用 日期计算器 推算某个时间戳前后若干天的日期。
2038 年问题:32 位系统的”千年虫”
老系统用 32 位有符号整数存储秒级时间戳,它能表示的最大值是 2147483647,对应 2038 年 1 月 19 日 03:14:07 UTC。下一秒,计数器溢出变成负数 -2147483648,时间会被解释为 1901 年 12 月 13 日。这就是著名的 2038 年问题(Year 2038 problem),逻辑上和 2000 年”千年虫”一模一样,只是换了个位数。
听起来还很遥远?想想那些今天就要签订的 15 年期贷款合同、养老金系统、嵌入式设备和工业控制器——它们现在就要面对 2038 年之后的日期计算。好消息是现代 64 位系统用 64 位整数存时间戳,能表示的时间范围远超宇宙年龄,新写的代码基本无需担心。真正的风险在于长期不维护的遗留系统、固件和文件格式。如果你在做底层或嵌入式开发,存储时间字段时请确认类型是 64 位无符号或有符号整数,而不是历史遗留的 32 位 time_t。
为什么 Unix 时间不数闰秒
地球自转在缓慢变快变慢,为了让原子钟时间和天文时间对齐,国际机构会不定期地在 6 月 30 日或 12 月 31 日插入一个闰秒——那一天会真的出现 23:59:60 这一秒。自 1972 年以来已经插入过 27 次闰秒。
但 Unix 时间戳假装闰秒不存在:它规定每天都是精确的 86400 秒。这样做的好处是时间计算极其简单——两个时间戳相减除以 86400 就是相隔天数;代价是 Unix 时间与真实地球自转有最多一秒左右的漂移。各操作系统处理闰秒的方式不同(有的重复一秒,有的把一秒”涂抹”到一整天里),所以依赖严格单调递增时间的系统(比如金融交易、分布式日志)需要特别小心。值得一提的是,国际计量大会已决定 2035 年后停止插入闰秒,这个历史遗留问题正在走向终结。
日常调试技巧
最后总结几个处理时间戳的实用技巧:
- 先数位数:10 位是秒,13 位是毫秒,16 位是微秒。位数不对,先怀疑单位。
- 秒转毫秒是乘 1000 还是加三个零,在代码评审里值得多看一眼,这是 bug 高发区。
- 数据库里存时间戳还是 datetime?存时间戳(或 UTC datetime)永远是更稳的选择,时区留给展示层。
- 日志里看到看不懂的数字,直接扔进 时间戳转换工具,它会同时给出 ISO、UTC 和本地时间三种格式,一眼就能确认是哪个时刻。
- 需要算”N 天后是几号”或两个日期间的间隔,用 日期计算器 比手算快,也不会被大小月和闰年绊倒。
- 跨国协作安排会议时,先在 世界时钟 里核对各方时区,避免”你说的下午是哪天下午”的经典误会。
时间戳是现代软件里最成功的约定之一:一个整数走天下。理解了纪元、单位、时区和 32 位限制这四件事,你遇到的绝大多数时间问题都会有清晰的排查路径。