单位错误的麻烦之处在于,它往往不会报错。程序照常运行,页面照常显示,只是日期悄悄跑到了 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 北京时间)为例:
1767225600 秒
→ 2026-01-01 08:00:00(北京时间)
→ 2026-01-01 00:00:00 UTC1767225600 毫秒
→ 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 三步验证,足够应付大多数场景
- 看来源:接口文档、数据库字段名、语言默认 API,哪一个更可能决定单位?
- 看数量级:换算结果是否落在业务时间附近?如果落到 1970 或公元 5000 年以后,立即回退检查单位。
- 反向验证:用同一个时间点的秒值和毫秒值分别转换,结果应相差 1000 倍但显示同一时刻。
需要快速验证时,可以把两个值一起粘贴到批量转换里,选择“自动识别”,观察每一行的输出;如果单位统一但数据很多,直接锁定单位会更稳妥。单个值则可以用转换工作台手动切换单位,结果会即时更新。