翻日志的时候经常看到这种数字:1760947200。知道它是时间,但不知道是哪天,
得去换算一下才认得出。
时间戳的设计其实很聪明:用单个数字记录时间点,没有格式歧义、没有时区歧义, 比较大小、算差值都是纯算术。但正因为「太简单」,用错的方式也特别隐蔽。
它到底是什么
Unix 时间戳 = 从 1970 年 1 月 1 日 00:00:00 UTC 起,到现在经过的秒数。
为什么是 1970?因为 Unix 系统在 1971 年发布,设计者取了一个「足够近又足够整」的起点。 这个日期本身没什么特殊含义,纯粹是工程上的选择。
坑一:秒和毫秒差 1000 倍
同一个时刻,秒级是 1760947200(10 位),毫秒级是
1760947200000(13 位)。看起来只是多了三个零,但解释错了,
时间会跑到完全离谱的地方。
- 把秒当毫秒:
1760947200会被理解成 1970 年 1 月 21 日—— 差了 55 年,而且落在 1970 年附近这个特征非常明显。 - 把毫秒当秒:
1760947200000按秒解释,会得到公元 57772 年。 这个更夸张,但有些系统的处理方式是直接报错或者归零,反而不容易发现。
经验法则:10 位是秒,13 位是毫秒。位数介于中间(11、12 位)说明这个时间 在 2286 年到 33658 年之间——基本可以断定是数据错了。
为什么会有两种?因为不同语言的默认行为不一样。Java 的 System.currentTimeMillis()
和 JavaScript 的 Date.now() 返回毫秒;Python 的 time.time()
和绝大多数 C 接口返回秒。跨语言对接时,这一层几乎总要出错一次。
坑二:时间戳没有时区,显示才有
这一点最容易被误解。同一个时间戳 1760947200:
- 在北京(UTC+8)显示为 2025-10-20 16:00
- 在伦敦(UTC+0)显示为 2025-10-20 08:00
数字只有一个,显示出来的样子有好几个。所以排查「时间对不上」的问题时, 正确的顺序是:先确认两边显示的是哪个时区,再怀疑数据本身。
很多线上事故的根因就是这一条:服务器用 UTC,日志按 UTC 打印, 运营同学按北京时间去核对,怎么都对不上,最后发现差了整整 8 小时。
坑三:2038 年问题
如果时间戳用 32 位有符号整数存储,最大值是 2147483647,
对应 2038 年 1 月 19 日 03:14:07 UTC。再往后一秒,就会溢出成负数,
时间直接跳回 1901 年。
这和 Y2K(千年虫)是同一类问题:当年为了省空间用两位存年份,结果 2000 年变成 1900 年。 现在大部分 64 位系统已经改用 64 位时间戳(能表示到约 2920 亿年后), 但嵌入式设备、老旧数据库字段、某些协议的固定 4 字节字段里,这个隐患还在。
如果你在维护用 int 存时间戳的表,现在就该考虑迁到 bigint。
距离 2038 年还有十二年,听起来很远,但数据迁移、兼容性测试、灰度发布加起来,
不会比这更短。
坑四:闰秒
Unix 时间在定义上忽略闰秒:每一天都当作正好 86400 秒。 而 UTC 因为有闰秒,实际某一天可能是 86401 秒。
结果是:从 1972 年到现在,Unix 时间累计比 UTC 慢了约 27 秒。 日常应用完全感知不到,但做天文观测、卫星轨道、高频交易这类需要精确对齐的场景, 必须用 TAI(国际原子时)或者专门的时间同步方案,不能用 Unix 时间戳硬扛。
一个自查习惯
看到任何时间戳,先数位数:10 位秒、13 位毫秒。然后换算出来看一眼年份对不对—— 如果落在 1970 年或者几万年后,基本就是单位搞错了。
工具里我让它同时显示本地时间和 UTC,就是为了省掉「脑子换算时区」这一步。 排查问题的时候,两个都摆出来,对不上的地方会自己跳出来。