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 裝置,以及數十年未更新的軟體。現代作業系統、語言與資料庫已經安全。