Unix 时间戳详解:秒与毫秒、时区和 2038 年问题
1700000000 是什么时候?为什么有些系统用 13 位数字?为什么时间恰好差了 8 个小时?弄懂几个概念,大部分时间相关的 bug 就会消失。
什么是 Unix 时间戳
Unix 时间戳(Unix 时间、纪元时间)是一个整数,表示自 1970 年 1 月 1 日 00:00:00 UTC(即 Unix 纪元)以来经过的秒数。0 是 1970-01-01 00:00:00 UTC,1700000000 是 2023-11-14 22:13:20 UTC。
用一个数字表示某一时刻有很多好处:两个时刻的差只需一次减法,排序和比较非常简单,而且无论时区或日期格式如何,同一时刻在任何地方的值都相同。
0 → 1970-01-01 00:00:00 UTC
1700000000 → 2023-11-14 22:13:20 UTC
1735689600 → 2025-01-01 00:00:00 UTC
2147483647 → 2038-01-19 03:14:07 UTC
10 位还是 13 位:秒还是毫秒?
当前以秒为单位的时间戳是 10 位。但 JavaScript 的 Date.now() 和 Java 的 System.currentTimeMillis() 返回的是毫秒,也就是 13 位。两者混淆会得到 1970 年 1 月 21 日或五万年后的日期。
数一数位数:10 位是秒,13 位是毫秒,16 位是微秒。如果 API 文档没有说明单位,务必核实,换算时乘以或除以 1,000。
时间戳没有时区
最重要的一点是:时间戳没有时区。1700000000 在北京和纽约是同一时刻。只有把数字显示为人能读懂的日期时才会应用时区:同一个值在 UTC 是 22:13,在中国(UTC+8)则是次日早上 06:13。
经典的“差了 8 小时”bug 就源于违反这条规则——存的是 UTC 却当作本地时间显示,或者存了不带偏移量的本地时间字符串却当作 UTC 读取。存储和传输使用 UTC(或时间戳),显示时使用用户所在的时区。
夏令时陷阱
中国目前不实行夏令时,但美国和欧洲大部分地区会在春季把时钟调快、秋季调回。因此“纽约比北京晚 13 小时”只在一年中的部分时间成立,其余时间相差 12 小时。
请使用 America/New_York 这样的 IANA 时区名称,而不是 -05:00 这样的固定偏移量,这样切换会被自动处理。与海外团队约会议或设置定时任务时,请用时区计算器确认当天的实际偏移量。
2038 年问题
如果用有符号 32 位整数存储,时间戳的最大值是 2,147,483,647,即 2038 年 1 月 19 日 03:14:07 UTC。再过一秒,数值就会溢出变成负数,跳回到 1901 年。这就是 2038 年问题。
现代操作系统和语言大多已使用 64 位时间,但 32 位的值仍残留在老旧嵌入式设备、数据库 INT 列和某些文件格式中。处理遥远未来日期的系统——例如长期有效的证书或 30 年期贷款——现在就可能触及上限,因此新设计应使用 64 位整数或正规的日期类型。
闰秒与精度
Unix 时间把每天都视为正好 86,400 秒,忽略闰秒。因此它可能与天文意义上精确的流逝时间有极小差异,普通应用可以放心忽略。国际计量大会已决定在 2035 年前取消闰秒。
把数字粘贴到 AIBIZAP 时间戳转换工具中,它会自动判断是秒还是毫秒,并显示 UTC 和所选时区的日期。比较各地时间可用时区计算器,定时重复的任务可用 cron 生成器。