2038 年问题(Y2K38)详解
2038 年 1 月 19 日 03:14:07 UTC,Unix 时间戳 2,147,483,647 —— 32 位有符号整数的最大值 —— 将会溢出。本文说明这意味着什么、哪些系统受影响,以及为什么大多数现代代码已经安全。
什么是 2038 年问题?
Unix 时间戳传统上以 32 位有符号整数存储,计数自 1970 年 1 月 1 日 00:00:00 UTC 以来的秒数。32 位有符号整数的最大值是 2,147,483,647,对应 2038 年 1 月 19 日 03:14:07 UTC。其后 1 秒,该整数溢出为 -2,147,483,648。作为 Unix 时间戳解读时,这个负值对应 1901 年 12 月 13 日——把受影响的系统送回约 137 年前。
溢出的数值
- 2^31 − 1 = 2,147,483,647(32 位有符号整数最大值)
- 2,147,483,647 作为日期 = 2038 年 1 月 19 日 03:14:07 UTC
- 2,147,483,647 + 1 溢出为 −2,147,483,648(最小的 32 位值)
- −2,147,483,648 作为日期 = 1901 年 12 月 13 日 20:45:52 UTC
哪些系统有风险?
该问题影响以 32 位有符号整数存储时间戳的系统:
- 使用 32 位 time_t 的嵌入式系统和微控制器
- 为 32 位目标编译、使用 time_t 或 int 存储时间戳的遗留 C/C++ 代码
- 仍在生产中的 32 位硬件上的旧版 Linux 内核(5.6 之前)
- MySQL TIMESTAMP 列——上限为 2038-01-19;应改用 DATETIME
- 一些以 32 位值编码时间戳的网络协议和文件格式
- 寿命长达数十年的工业控制系统和 IoT 设备固件
哪些系统是安全的?
大多数现代代码和基础设施已不受影响:
- 任何 64 位操作系统——Linux(glibc 2.34+ 也修复了 32 位硬件)、macOS、Windows
- Python——时间戳使用 64 位浮点数,远超公元 2.92 亿年仍安全
- JavaScript——Date 使用 64 位 IEEE 754 浮点数,安全到公元 275,760 年
- Go 与 Rust——内部用 int64 表示时间,可安全数十亿年
- Java——java.time.Instant 使用 long(64 位),安全到公元约 2.92 亿年
- PostgreSQL TIMESTAMP——可正确存储 2038 年以后的日期
- MySQL DATETIME——范围 1000-01-01 至 9999-12-31,不受影响
2038 年问题常见问题
- 什么是 2038 年问题?
- 2038 年问题(Y2K38)是一个 bug:使用 32 位有符号整数存储 Unix 时间戳的系统会在 2038 年 1 月 19 日 03:14:07 UTC 溢出。32 位有符号整数的最大值是 2,147,483,647。1 秒后,该整数回绕到 -2,147,483,648,对应 1901 年 12 月 13 日。
- 2038 年 1 月 19 日的 Unix 时间戳是多少?
- 2038 年 1 月 19 日 03:14:07 UTC 的 Unix 时间戳是 2,147,483,647——32 位有符号整数的最大值(2^31 - 1)。这是 32 位系统上发生 2038 年溢出的确切时刻。
- Y2K38 和 Y2K 一样吗?
- 不一样。Y2K(2000 年问题)是程序用两位数存储年份造成的。Y2K38 是把 Unix 时间戳存为 32 位有符号整数、在 2038 年溢出造成的。根因不同,受影响的系统也不同。
- 2038 年互联网会崩溃吗?
- 大概率不会有重大影响。大部分互联网基础设施已迁移到 64 位系统。风险集中在嵌入式系统、老旧 IoT 设备,以及数十年未更新的软件。现代操作系统、语言和数据库已经安全。