Unix 时间戳到底是什么:从秒级毫秒级到 2038 年问题
讲清 Unix 时间戳的含义、10 位秒级和 13 位毫秒级怎么区分、时区与 UTC 的关系,以及为什么 32 位系统会在 2038 年溢出,配真实转换例子。
Unix 时间戳到底是什么:从秒级毫秒级到 2038 年问题
我第一次被时间戳坑,是在排查一条日志的时候。一列数字写着 1700000000,程序里另一处却是 1700000000000,我盯着多出来的三个零想了半天才反应过来,它们指的是同一刻。这类混淆每个写代码的人都会遇到一次。下面把 Unix 时间戳讲透,省得你也对着数字发呆。
Unix 时间戳是一个计数器
Unix 时间戳的本质很简单:它是从某个固定起点开始,过去了多少秒。这个起点叫 Unix 纪元(epoch),被定为 1970 年 1 月 1 日 00:00:00 UTC。所以 1700000000 的意思就是,从 1970 年那一刻起,已经过去了十七亿秒。
为什么选 1970 年?这是 Unix 操作系统早期开发时定下的约定,后来被几乎所有系统继承,成了事实标准。它的好处是:无论世界上哪个时区,同一时刻对应的时间戳完全一样。它不带年月日,不带时区,只是一个干净的整数,机器存它、比它、算时间差都极快。
秒级与毫秒级:10 位和 13 位
这是最常见的坑。Unix 时间戳原本以秒为单位,但 JavaScript、Java 这些语言习惯用毫秒,于是同一时刻就有了两种写法:
- 秒级:
1700000000,通常 10 位 - 毫秒级:
1700000000000,通常 13 位,正好是秒级乘以 1000
判断方法很直接:看位数。一个小于 1e10(约公元 2286 年 11 月)的数按秒解析;更长的按毫秒。如果把 13 位的 1719820800000 当成秒去读,日期会落到公元 56489 年,数据导入时这种错误会悄无声息地写进数据库。Postgres 的 to_timestamp()、Go 的 time.Unix(s, 0) 收的是秒,而 new Date(ms)、Java 的 Instant.ofEpochMilli 收的是毫秒。写入前先确认单位,能省掉一次半夜回滚。
一个真实的转换例子
把 1700000000 粘进 /zh/t/timestamp-converter/,它会同时给出两行:
- UTC:2023-11-14 22:13:20
- 本地(东八区):2023-11-15 06:13:20
同一个整数,UTC 和本地相差正好 8 小时。如果你只看本地时间,很容易把日子算错一天,这正是下一节要说的问题。
时区与 UTC:时间戳本身不带时区
关键认知:Unix 时间戳永远是 UTC 的,它本身不携带任何时区信息。时区是在显示这一步才叠加上去的。
我自己踩过最典型的一次,是排查 JWT 为什么提前过期。token 的 exp 字段是 1719820800,网关判定已过期,可我算着还有几小时。粘进转换工具一看,它对应 UTC 的 2024-07-01 08:00:00,也就是东八区的 16:00,而我一直把它当成本地早上 8 点在读。错就错在,把 UTC 的 epoch 当成了本地时间。所以动手减时差之前,务必先看 UTC 那一列。判断 token 内容时,/zh/t/jwt-decoder/ 能把 exp、iat 这些字段连同时间戳一起解出来,配合时间戳转换一起用很顺手。
2038 年问题:32 位的尽头
时间戳是整数,而整数有上限。早期系统用 32 位有符号整数(time_t 定义为 int32)存储秒数,它能表示的最大正值,换算成时间是 2038 年 1 月 19 日 03:14:07 UTC。再往后加一秒,这个数就会溢出,翻转成一个负数,日期被解读成 1901 年。这就是著名的 2038 年问题,本质和 2000 年千年虫是同一类:存储位宽不够用了。
现代 64 位系统和用 64 位整数存时间戳的语言不受影响,可表示的范围远超人类文明的时间尺度。但仍有大量嵌入式设备、老旧固件、用 int32 写死的协议还在跑,它们到 2038 年会集体出错。这不是危言耸听,而是一个有明确日期的硬约束,届时该升级的系统现在就该排进计划。
负时间戳:1970 之前怎么办
既然纪元是 1970 年,那 1970 之前的时间怎么表示?答案是负数。-2208988800 对应 UTC 的 1900-01-01 00:00:00。很多解析器遇到负 epoch 会报错或直接返回 1970,导致一批历史数据全部显示成「1969-12-31」。如果你在迁移老系统的出生日期、档案日期,负时间戳的支持就很关键,它能告诉你老系统用的到底是不是 1970 之前的纪元。
小结
时间戳是个朴素的整数计数器,但围绕它的三件事最容易出错:单位是秒还是毫秒(10 位还是 13 位)、它永远是 UTC 而非本地、以及 32 位系统在 2038 年的天花板。把这三点记牢,日志和接口里的数字就不再神秘。处理时间区间长度时,/zh/t/duration-converter/ 可以把秒、分、小时、天互相换算,和时间戳一起用能覆盖大部分日常场景。
Made by Toolora · Updated 2026-06-13