发布于 2026-05-23
时间戳问题为什么这么多
JavaScript 的 Date、Python 的 datetime、Java 的 LocalDateTime、MySQL 的 DATETIME —— 每种语言 / 数据库对时间的抽象都不一样,而它们却经常需要互通。
下面是 6 个真实踩过的坑。
坑 1:UTC 和本地时间混用
症状:同一个 Unix 时间戳,前端显示 14:00,后端日志写的是 06:00,差 8 小时。
原因:服务器 OS 设的是 UTC,前端跑用户浏览器时区(GMT+8)。两边都对,只是没说清显示的是哪个时区。
修法:
- 存数据库永远用 UTC(MySQL
TIMESTAMP字段自动是 UTC) - API 返回 Unix 时间戳(秒或毫秒数字,无时区歧义)
- 前端展示时才转用户本地时区:
new Date(timestamp * 1000).toLocaleString()
调试:把疑似有问题的时间戳粘到 时间戳转换 查看,工具同时显示本地时区 + UTC,一眼看清。
坑 2:Unix 时间戳的 10 位 vs 13 位
症状:new Date(1779489600) 显示 1970 年,但同事说应该是 2026 年。
原因:JavaScript Date 接收毫秒时间戳(13 位),你给的是秒(10 位)。
new Date(1779489600) // 1970-01-21 18:38(把秒当毫秒了)
new Date(1779489600 * 1000) // 2026-05-22 12:00 ✓
修法:
- 看到 10 位时间戳,记得乘 1000
- Java
System.currentTimeMillis()是毫秒,Pythontime.time()是秒(浮点),Gotime.Now().Unix()是秒,各语言不一
自动识别:时间戳转换工具 会自动按长度判断秒 / 毫秒,不用记。
坑 3:跨日变量
症状:用户报「我 5 月 1 日下午 10 点下的单,系统显示是 5 月 2 日」。
原因:服务器跑 UTC,5 月 1 日 22:00 中国 = 5 月 1 日 14:00 UTC,没问题。但有人把 UTC 时间直接拿去 .format('YYYY-MM-DD'),没转用户时区就格式化,结果跨日。
修法:格式化"日期"时必须显式指定时区。dayjs / moment 都支持:
dayjs(timestamp).tz('Asia/Shanghai').format('YYYY-MM-DD')
调试:把这种边界时刻丢 跨时区计算 比对,横排看不同时区的日期到底是哪天。
坑 4:夏令时(DST)切换日
症状:报表统计「凌晨 2-3 点订单数」,某天数据是平时的 0 或 2 倍。
原因:夏令时切换日(美国春季):本地时间从 02:00 直接跳到 03:00,2 点 -3 点这段时间根本不存在。秋季反过来,02:00-03:00 重复出现 1 次,数据翻倍。
修法:
- 报表按 UTC 小时统计,不按本地时间
- 或显式声明「按 EST 时间统计,夏令时切换日为预期异常」
中国不行夏令时,但只要业务覆盖美国 / 欧洲就要处理。
坑 5:数据库时区设置不对
症状:SELECT NOW() 在 MySQL 里返回的时间和 OS 时间差了几小时。
原因:MySQL time_zone 设置和服务器 OS 不一致:
SHOW VARIABLES LIKE 'time_zone'; -- 看当前 MySQL 时区
SET time_zone = '+08:00'; -- 临时改成北京时间
修法:
- 推荐
time_zone = 'UTC',避免任何隐式转换 TIMESTAMP字段会自动按 session time_zone 转,DATETIME字段不会(纯文本存)- 跨地区项目优先用
TIMESTAMP字段
坑 6:cron 表达式时区
症状:跑了个 cron 0 9 * * *(每天 9 点跑),但实际触发是 17:00。
原因:cron 默认按系统时区触发,服务器是 UTC → 9:00 UTC = 17:00 北京。
修法:
linux 系统 cron:
TZ=Asia/Shanghai写在 crontab 顶部node-cron / cronstrue:每个 schedule 单独传 timezone 参数
cron.schedule('0 9 * * *', task, { timezone: 'Asia/Shanghai' })或者统统按 UTC 思考,要北京 9 点就写
0 1 * * *(UTC 1 点)
调试:用 Cron 表达式工具 输入表达式,显示「下一次触发时间」,看是不是预期。
速查表
| 现象 | 大概率原因 | 修法 |
|---|---|---|
| 时间差 8 小时 | UTC vs 本地时区 | 显式声明时区 + Unix 时间戳传值 |
| 1970 年 | 秒 vs 毫秒 | 乘 / 除 1000 |
| 跨日不对 | 没转时区就格式化 | tz('Asia/Shanghai') 后再 format |
| DST 切换日数据异常 | 夏令时跳 / 重 | 按 UTC 小时统计 |
NOW() 不对 |
数据库 time_zone | SET time_zone = '+08:00' 或全 UTC |
| cron 触发不对 | cron 时区 | crontab 加 TZ=... 或 schedule 加 timezone |
工具组合
排查时间问题时,以下 3 个工具配合使用:
把可疑数据 / 表达式分别丢进去对比,通常 5 分钟内能定位问题。
一条核心建议
永远不要在内部传"日期字符串",传 Unix 时间戳数字。
- 数字无歧义,绝对时刻
- 字符串
2026-05-23 14:00既可能是 UTC 也可能是本地时区,处理一不小心就错 - 字符串只在最外层展示前转(知道用户是哪个时区时)
记住这一条,80% 时间问题不会发生。