“服务器记录的下单时间戳是 1713772800,用户说他是下午 5 点下的单,但我本地转出来是上午 9 点,谁错了?“——都没错。这是时区。
Unix 时间戳是什么
从 UTC 1970-01-01 00:00:00 开始经过的秒数。这个定义里有两个关键点:
- 起点是 UTC 零点(不是北京时间)
- 时间戳本身是一个数字,不带时区信息
所以 1713772800 这个数字在地球上任何地方都代表同一个绝对时刻。只是显示成”人类看得懂的时间”时,才需要加上时区才能算出 几点几分。
同一时间戳在不同时区
1713772800 这个数字:
| 时区 | 显示 |
|---|---|
| UTC | 2024-04-22 08:00:00 |
| 北京 (UTC+8) | 2024-04-22 16:00:00 |
| 纽约 (UTC-4) | 2024-04-22 04:00:00 |
| 东京 (UTC+9) | 2024-04-22 17:00:00 |
没有哪个是”正确”的,只是观察角度不同。
秒 vs 毫秒:两位之差多 1000 倍
- 10 位数字(
1713772800)= 秒级时间戳,PHP/Python 默认 - 13 位数字(
1713772800000)= 毫秒级,JavaScript/Java 默认
写代码时踩坑:JavaScript 的 Date.now() 返回毫秒,而 Python 的 time.time() 返回秒(浮点)。两边接口对接时必须对齐单位。
一秒差对比一年时长:
- 如果你把秒级戳当毫秒用:显示的时间是 1970-01-20 左右(起点后几千秒)
- 如果你把毫秒级戳当秒用:显示的时间是 56000 年之后
出现”离谱时间”基本就是这个错误。
常见踩坑清单
1. 数据库存字符串 2024-04-22 16:00:00,没记时区
别人来读这个值,不知道是北京时间还是服务器本地时间。最佳实践:永远存 UTC 时间戳(或带时区的 ISO 8601 字符串)。
2. JavaScript new Date('2024-04-22')
这个写法会被解析成 UTC 零点(2024-04-22T00:00:00Z),在北京时区显示成 4 月 22 日早 8 点。要表达”本地的 4 月 22 日零点”得写 new Date('2024-04-22T00:00:00')(注意没有 Z)。
3. 夏令时 UTC+8 的中国不用夏令时,但美国欧洲一年两次切换。同一个”本地时间 9:00”在夏令时和冬令时对应的时间戳不一样。后端处理国际用户时别按本地时间做调度,全部换成 UTC 再运算。
时间戳永远是 UTC 秒
当你看到时间戳 1713772800,它就是一个绝对时刻。想看北京时间还是纽约时间,是显示层的问题,不是数据本身的问题。这个认知到位了,90% 的时区 bug 就不会再出现。
互转 + 多时区对照
输入时间戳一次性显示 UTC、北京、纽约、东京等多个时区的结果;反过来输入本地时间指定时区也能算出对应时间戳。