cv.cm

知识库 · 日期与时间

什么是 Unix 时间戳?

阅读约 6 分钟

简短回答

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 日期和时间备注
01970-01-01T00:00:00ZUnix 纪元
10000000002001-09-09T01:46:40Z第一个 10 位数值
12345678902009-02-13T23:31:30Z广为人知的里程碑
17000000002023-11-14T22:13:20Z最近的整数值
17919360002026-10-14T00:00:00Z2026 年 10 月 14 日 UTC 零点
21474836472038-01-19T03:14:07Z有符号 32 位整数的最大值

秒、毫秒及其他单位

经典的 Unix 时间戳以秒为单位,但很多系统会存储更细的单位。对于当前日期,看位数是最快的区分方法:

单位当前位数同一时刻的示例常见于
秒101700000000Unix 工具、PHP time()、Python time.time()(浮点数)、JWT 的 exp 声明
毫秒131700000000000JavaScript Date.now()、Java System.currentTimeMillis()
微秒161700000000000000PostgreSQL 内部、部分日志系统
纳秒191700000000000000000Go 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 年以前的日期前请先测试。

全部知识文章