开发里有两种 bug 最让人崩溃:一种是偶现的,一种是“我明明没改任何逻辑但线上报表突然全乱了”。时间问题经常两种都占。

你会看到订单创建时间比支付时间还晚、定时任务在某一天执行两次、用户说“我昨天明明下单了怎么显示今天”、日志排查像在解谜……

这篇文章我按“高频翻车点”来列清单:每个坑我都给你为什么会发生、怎么复现、怎么规避。写完你就能直接拿去做代码评审 checklist。

坑 1:UTC 和本地时间混用(最常见,也最隐蔽)

同一个系统里,一部分模块用 UTC,一部分模块用本地时间,最开始可能看不出来,一旦跨地域部署/多时区用户/对账报表出现,就会全线开花。

典型症状:

  • 同一条记录在不同页面展示的时间差 8 小时
  • 按“当天”统计的报表在午夜附近错位
  • 本地开发正常,容器/服务器部署后全乱

规避策略(我自己最推荐的写法):

  • 系统内部统一以 UTC(或带偏移的时间)为“存储/传输/计算”标准
  • 只在展示层按用户时区转换
  • 任何时间字段命名明确:createdAtUtc / createdAtLocal 这种就很直观

坑 2:时间戳秒/毫秒搞反(差 1000 倍的经典)

你见过“1970年”或“51382年”吗?大概率不是穿越,是秒/毫秒搞反了。

类型 常见位数 例子
秒时间戳 10 位左右 1700000000
毫秒时间戳 13 位左右 1700000000000

规避策略:

  • 接口文档明确单位:timestampSeconds / timestampMillis
  • 服务端做输入校验:长度明显不对直接拒绝或纠正
  • 日志里打印时间戳时同时打印解析后的 ISO 时间,排查更快

坑 3:把时区写成固定偏移(+08:00/-05:00)

固定偏移看起来很省事,但它忽略了夏令时(DST)。比如“纽约=UTC-5”只在一段时间里成立,夏令时期间会变成 UTC-4。

规避策略:

  • 用 IANA 时区(例如 Asia/Shanghai、America/New_York)而不是写死偏移
  • 时间转换用“城市/地区”做时区来源,别用“我记得差 12 小时”
  • 需要临时换算时可用时区转换器核对

坑 4:夏令时切换导致“某个时间不存在/出现两次”

这是最诡异的一类 bug,因为它不是每天发生,而是某个周末突然发生。

两个关键现象:

  • 跳过的一小时:比如从 01:59 直接跳到 03:00,这段本地时间“根本不存在”
  • 重复的一小时:比如 01:30 会出现两次,你如果只存“本地时间”就会歧义

最容易翻车的场景:

  • 定时任务按本地时间执行:可能重复执行或漏执行
  • 按本地日期分组报表:某天会多一小时或少一小时
  • 计费按小时:切换日直接对不上账

规避策略:

  • 系统内部用 UTC 做锚点,调度与计费尽量按 UTC 计算
  • 展示层用用户时区转换,并且对“歧义时间”有明确处理策略
  • DST 切换周加一组回归测试(真的很值)

坑 5:ISO 8601 解析差异(字符串看着一样,结果不一样)

同样是 “2026-02-20 10:00:00”,不同语言/库/配置可能会:

  • 当作本地时间解析
  • 当作 UTC 解析
  • 直接解析失败(尤其是缺少 T、缺少 Z、或格式不标准)

规避策略:

  • 传输格式统一使用 ISO 8601,并且明确包含时区信息(例如带 Z 或带偏移)
  • 避免传“裸时间字符串”(没有时区信息的时间)
  • 落库前把输入解析成统一的时间类型,再序列化

坑 6:数据库/ORM/驱动的默认时区不一致

很常见的情况是:数据库以某个时区存,应用以另一个时区读,ORM 再帮你“贴心转换”一次,最终你看到的时间就变成“看起来像对的错”。

规避策略:

  • 明确数据库会话时区、连接参数、以及应用运行时区(容器里尤其要注意)
  • 在迁移/初始化里设置一致的时区策略,不要靠“默认值刚好是UTC”
  • 关键表加一条对照数据做健康检查(比如固定 UTC 时间点,校验读写一致)

坑 7:按“日期”做业务规则,但用户在不同时区

比如“每天签到”“当日订单”“每日限额”,如果你用服务器日期判断,海外用户很容易出现:

  • 明明还没到他那边的 0 点,系统却提示“今天已用完”
  • 他那边已经第二天了,系统还当成昨天

规避策略:

  • 用户相关的“日期边界”,以用户时区为准
  • 报表/统计明确:是按UTC日、按业务时区日、还是按用户本地日

坑 8:日志排查只看字符串时间,不看时区

排查线上问题时,如果日志里只有 “2026-02-20 10:00:00”,你根本不知道它是哪儿的 10 点。

规避策略:

  • 日志统一输出 ISO 8601 + 时区(或直接输出 UTC)
  • 关键链路同时打印毫秒时间戳,便于跨系统对齐
  • 排查跨地域问题时,用工具快速对表:时区转换器

可复制的“时间转换代码评审清单”

检查项 问一句就能发现问题
存储标准 这里存的是UTC还是本地?有偏移吗?
输入单位 时间戳是秒还是毫秒?有校验吗?
时区来源 用的是IANA时区还是写死偏移?
DST处理 切换日会不会重复/漏执行?
序列化格式 时间字符串包含时区信息吗?
用户日期边界 “每天”是按谁的0点?

总结

时间转换的坑,本质是“你以为大家说的是同一个时间”。只要你把系统规则定死:内部用UTC、展示按用户时区、接口带时区信息、输入输出有校验,大多数事故都能提前消灭。

如果你想把时区基础再补一补,也可以先看这篇:UTC 和 GMT 有什么区别?