NOTE 01 / UNIT

10 位还是 13 位?
时间戳单位判断与换算笔记

“10 位是秒,13 位是毫秒”这句话在大多数时候够用,但真到接口联调时,只数位数仍然可能踩坑。单位问题的代价通常是整整 1000 倍的时间偏移,所以值得用一套更稳的判断顺序。

单位错误的麻烦之处在于,它往往不会报错。程序照常运行,页面照常显示,只是日期悄悄跑到了 1970 年或几万年以后。等发现时,可能已经有一批数据被写入。

01 位数只是线索,不是规则

2026 年前后,秒级时间戳是 10 位,毫秒级是 13 位。这个规律来自当前时间点的数量级:秒级约为 1.7×10⁹,毫秒级约为 1.7×10¹²。但时间在走,位数也会变化。到了 2286 年,秒级时间戳会变成 11 位;而 1970 年附近的毫秒值可能只有 11 位或 12 位。

负数时间戳更让“数位数”变得别扭。-1 只有 2 个字符,却明确表示 1969 年的一个瞬间。因此,位数只能作为第一眼提示,不能作为唯一依据。

02 用数量级判断,而不是字符数

更可靠的判断是看数值大致落在哪个区间。以当前时间为参照:

数值范围更可能的单位换算后的时间
约 10⁹秒2020 年代
约 10¹²毫秒2020 年代
约 10¹⁵微秒2020 年代
约 10¹⁸纳秒2020 年代

如果一个 13 位数字被当作秒,结果会落在公元 5000 年以后;如果一个 10 位数字被当作毫秒,结果会回到 1970 年 1 月。这两个特征非常明显,适合作为快速自检。

03 1000 倍误差长什么样

以 1767225600(2026-01-01 08:00:00 北京时间)为例:

CORRECT / 按秒解释
1767225600 秒
→ 2026-01-01 08:00:00(北京时间)
→ 2026-01-01 00:00:00 UTC
WRONG / 把秒当成毫秒
1767225600 毫秒
→ 1970-01-21 18:53:45.600(北京时间)
→ 早了五十多年

反向的错误同样醒目:把 1767225600000 当成秒,会得到远超 JavaScript 可靠范围的年份。遇到这类结果,不要怀疑日历,先检查单位开关。

04 字段命名与存储,比注释更可靠

如果字段只叫 time 或 timestamp,下一位维护者仍然需要猜。更清晰的做法是把单位写进名字:

  • created_at:带时区的时间类型或 ISO 字符串;
  • created_at_s:秒级整数;
  • created_at_ms:毫秒级整数;
  • duration_us:微秒级时长,和“时间点”区分开。

数据库里,秒和毫秒都应使用足够宽的整数类型,例如 BIGINT,而不是 32 位 INT。如果业务需要毫秒精度,时间类型也要选择带小数秒的版本,否则写入时会被悄悄截断。

05 精度与安全整数

JavaScript 的 Number 能精确表示的安全整数上限是 2⁵³−1,约 9.007×10¹⁵。微秒值通常在 10¹⁵ 量级,已经接近边界;纳秒值则明显超过。把 19 位纳秒值直接放进 JSON Number,可能损失精度。

因此,超过 13 位的整数时间戳在跨语言传递时,优先使用字符串,或在支持的语言里使用 BigInt / int64。前端展示日期时,再转换成毫秒。转换过程要明确记录的精度损失,而不是假设“数字看起来没变就没事”。

06 三步验证,足够应付大多数场景

  1. 看来源:接口文档、数据库字段名、语言默认 API,哪一个更可能决定单位?
  2. 看数量级:换算结果是否落在业务时间附近?如果落到 1970 或公元 5000 年以后,立即回退检查单位。
  3. 反向验证:用同一个时间点的秒值和毫秒值分别转换,结果应相差 1000 倍但显示同一时刻。

需要快速验证时,可以把两个值一起粘贴到批量转换里,选择“自动识别”,观察每一行的输出;如果单位统一但数据很多,直接锁定单位会更稳妥。单个值则可以用转换工作台手动切换单位,结果会即时更新。