Unix 时间戳:秒与毫秒

为什么 1700000000 和 1700000000000 都可能是日期,如何识别单位并避免经典的乘除 1000 错误。

Unix 时间戳到底是什么

Unix 时间戳是一个数字,表示从 1970-01-01 00:00:00 UTC(也叫 Unix 纪元)开始经过的秒数。它只是一个数字,不带时区和格式,也没有歧义:地球上任何地方看到同一个值,都代表同一个时刻。因此数据库、API 和日志系统更偏好时间戳,而不是像“03/04/2026”这样在美国和欧洲代表不同日期的字符串。

问题在于,“Unix 时间戳”并不只对应一种单位。不同生态系统会把基础单位放大,因此转换之前必须先确认手里的数字到底以什么为单位。

你会遇到的四种单位

一个快速的位数经验法则可以覆盖绝大多数情况:约 10 位是秒,13 位是毫秒,16 位是微秒,19 位是纳秒。如果转换后得到公元 56000 年的日期,或得到 1970 年附近的日期,通常是把单位猜错了 1000 倍。

  • 秒——经典形式。直到 2286 年通常是 10 位数。Unix 本身、大多数服务器日志、JWT 的 exp/iat 声明以及 PostgreSQL 的 epoch 函数都会使用它。
  • 毫秒——13 位数。JavaScript 标准(Date.now())、Java 和大多数 JSON API 常用的单位。
  • 微秒——16 位数。除 Python 的 time.time_ns() 外,一些数据库(如 Cassandra 和部分 MySQL 场景)会使用微秒。
  • 纳秒——19 位数。Go 的 time.Time 内部实现和高精度日志系统会使用它。

乘除 1000 的经典错误

生产环境中最常见的时间戳错误,是不同系统之间混用了秒和毫秒。比如 JavaScript 前端把 Date.now()(毫秒)发送给 Python 后端,后端没有除以任何数就按秒存储,于是每条记录都会突然落在 5.5 万年之后。反过来也一样:后端的秒数被 JavaScript 当成毫秒渲染,就会得到 1970 年 1 月附近的时间。

两种数字都是合法数值,因此语言本身不会发出警告。防范方法是在 schema 和变量名中明确写出单位:created_at_ms 比 created_at 更安全,并在系统边界处断言单位。

时区只是显示问题

时间戳本身没有时区,时区只在把它渲染给人看时才出现。应存储和传输时间戳(或带明确偏移量的 ISO 8601 字符串),并尽可能晚地转换到用户本地时区。不带时区保存“本地墙上时间”,正是夏令时切换导致数据中出现重复或消失小时的原因。

还有一个边界:Unix 时间忽略闰秒,因此闰秒期间的时间戳会与下一秒使用相同数值。这几乎不会影响日常工作,但能解释为什么几十年跨度的严格 UTC 计算和 Unix 时间计算可能相差几十秒。

参考资料

相关工具