知识库 · 日期与时间
什么是 Unix 时间戳?
简短回答
Unix 时间戳是从 1970 年 1 月 1 日 00:00:00 UTC(称为 Unix 纪元)起经过的秒数,不计闰秒。它是一个不带时区的整数,所以 1700000000 在世界各地都代表同一时刻:2023 年 11 月 14 日 22:13:20 UTC。JavaScript 和很多 API 使用的是毫秒,数字会多出三位。
这个数字是如何定义的
POSIX 用一个固定公式定义「自纪元以来的秒数」:每天恰好算作 86,400 秒,从 1970-01-01T00:00:00Z 开始计数。纪元之前的时刻为负数,所以 -1 是 1969-12-31T23:59:59Z。
由于这个值以 UTC 计量,它既不带时区,也不涉及夏令时。把它换算成当地时间是另一个步骤,需要指定时区,例如 Asia/Tokyo 或 America/New_York。
| 时间戳(秒) | UTC 日期和时间 | 备注 |
|---|---|---|
0 | 1970-01-01T00:00:00Z | Unix 纪元 |
1000000000 | 2001-09-09T01:46:40Z | 第一个 10 位数值 |
1234567890 | 2009-02-13T23:31:30Z | 广为人知的里程碑 |
1700000000 | 2023-11-14T22:13:20Z | 最近的整数值 |
1791936000 | 2026-10-14T00:00:00Z | 2026 年 10 月 14 日 UTC 零点 |
2147483647 | 2038-01-19T03:14:07Z | 有符号 32 位整数的最大值 |
秒、毫秒及其他单位
经典的 Unix 时间戳以秒为单位,但很多系统会存储更细的单位。对于当前日期,看位数是最快的区分方法:
| 单位 | 当前位数 | 同一时刻的示例 | 常见于 |
|---|---|---|---|
| 秒 | 10 | 1700000000 | Unix 工具、PHP time()、Python time.time()(浮点数)、JWT 的 exp 声明 |
| 毫秒 | 13 | 1700000000000 | JavaScript Date.now()、Java System.currentTimeMillis() |
| 微秒 | 16 | 1700000000000000 | PostgreSQL 内部、部分日志系统 |
| 纳秒 | 19 | 1700000000000000000 | Go time.UnixNano()、高精度链路追踪 |
单位混用是最常见的时间戳 bug。把秒值传给 JavaScript 的 new Date(),会得到 1970 年 1 月;把毫秒值当成秒读,则会跑到几万年以后。在 JavaScript 中,用 Math.floor(Date.now() / 1000) 得到秒,用 new Date(seconds * 1000) 转回来。
闰秒不计入
UTC 偶尔会插入一个闰秒,以与地球自转保持同步;自 1972 年以来共增加了 27 个闰秒。Unix 时间忽略闰秒:POSIX 公式假定每天都是 86,400 秒,所以闰秒要么被重复一次,要么被分摊到一段更长的时间里,具体取决于系统。
实际上,这意味着两个时间戳相减得到的经过时间,可能与真实值相差期间的闰秒数。对日常排程来说这点误差无关紧要;对科学或金融计时,系统会改用 TAI 或 GPS 时间。国际计时机构已于 2022 年投票决定,在 2035 年前停止增加闰秒。
2038 年问题
较老的软件用有符号 32 位整数存储 Unix 时间,其最大值为 2147483647,将在 2038-01-19T03:14:07Z 达到。再过一秒,计数器就会溢出为 -2147483648,被解读为 1901-12-13T20:45:52Z。
- 现代 64 位操作系统、编程语言和数据库使用 64 位的
time_t,可以用大约 2920 亿年。 - 风险仍存在于嵌入式设备、老旧文件格式、MySQL
TIMESTAMP这类 32 位数据库列,以及只给时间预留四个字节的二进制协议中。 - 20 年期证书、房贷和到期日等长期数值已经跨过 2038 年,所以这个 bug 可能远在那天之前就出现。
什么时候用时间戳,什么时候用日期字符串
时刻(事件发生的时间、令牌过期的时间)用 Unix 时间戳或 UTC 日期字符串存储。两者都没有歧义,也能正确排序。
在日志、API 和需要人阅读的文件中,用 2026-10-14T09:30:00Z 这样易读的 ISO 8601 字符串,因为 1791970200 一眼看不出是什么时间。未来的本地事件,比如明年春天巴黎 09:00 的会议,应保存为本地时间加 IANA 时区名:夏令时规则可能在那天到来前改变,预先算好的时间戳到时就会出错。
常见问题
Unix 时间戳一定是 UTC 吗?
是的。计数从 1970 年 1 月 1 日 UTC 零点开始定义,所以这个数字本身不带时区。只有把它换算成易读的当地时间时才需要时区。
如何获取当前的 Unix 时间戳?
在 Linux 或 macOS 终端运行 date +%s,在 JavaScript 中用 Math.floor(Date.now() / 1000),在 Python 中用 int(time.time())。三者返回的都是自纪元以来的秒数。
为什么我的时间戳转换出来是 1970 年?
这个值几乎可以肯定是以秒为单位,却被当成了毫秒读取。传给 JavaScript 的 new Date() 等需要毫秒的函数前,先乘以 1,000。
Unix 时间戳可以是负数吗?
可以。负值表示从纪元往前倒数,所以 -86400 是 1969 年 12 月 31 日 00:00 UTC。有些老系统不接受负值,用这种方式存储 1970 年以前的日期前请先测试。