2038 年問題(Y2K38)の解説
2038 年 1 月 19 日 03:14:07 UTC に、Unix タイムスタンプ 2,147,483,647 — 符号付き 32 ビット整数の最大値 — はオーバーフローし、1901 年の日付に巻き戻ります。本稿ではその意味、影響を受けるシステム、64 ビット time_t による修正の仕組み、そして自分のコードをテストする方法を説明します。
オーバーフローを 1 行で
符号付き 32 ビット整数は最大で 2,147,483,647 です。Unix 秒として保存すると、その値は 2038-01-19 03:14:07 UTC であり、その 1 秒後には -2,147,483,648 に巻き戻り、1901 年 12 月 13 日と読まれます。いまだに時刻を符号付き 32 ビット整数で保持しているシステムは、その瞬間に 137 年過去へ飛びます。修正はひと言、64 ビットです。下の表は、どのストレージ型が影響を受け、どれがすでに安全かを示します。
| ストレージ型 | オーバーフロー時期 / 最大日付 | 2038 年以降も安全? |
|---|---|---|
| 符号付き 32 ビット time_t / SQL INT | 2038-01-19 03:14:07 UTC | いいえ |
| MySQL TIMESTAMP | 2038-01-19 03:14:07 UTC | いいえ — DATETIME を使う |
| 符号なし 32 ビット | 2106-02-07 06:28:15 UTC | 68 年延びるが解決ではない |
| 符号付き 64 ビット time_t / SQL BIGINT | 約 2920 億年 | はい |
| 64 ビット浮動小数点(JavaScript、Python) | 西暦 275,760 年 | はい |
| MySQL DATETIME(パックされたフィールド) | 西暦 9999 年 | はい |
2038 年問題とは何か
Unix タイムスタンプは伝統的に、1970 年 1 月 1 日 00:00:00 UTC からの秒を数える符号付き 32 ビット整数として保存されます。符号付き 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
- オーバーフローの現地時刻は様々:1 月 18 日 22:14:07 EST、1 月 19 日 14:14:07 JST
どのシステムが危険か
この問題は、タイムスタンプを符号付き 32 ビット整数で保存するシステムに影響します。
- 32 ビット time_t を持つ組み込みシステムやマイコン
- time_t や int をタイムスタンプに使い、32 ビット向けにコンパイルされた古い C/C++ コード
- いまだ本番で稼働する 32 ビットハードウェア上の古い Linux カーネル(5.6 より前)
- MySQL TIMESTAMP 列 — 2038-01-19 までに制限。代わりに DATETIME を使う
- タイムスタンプを 32 ビット値でエンコードする一部のネットワークプロトコルやファイル形式
- 数十年の寿命を持つ産業用制御システムや IoT 機器のファームウェア
どのシステムが安全か
現代のコードとインフラの大半はすでに影響を受けません。
- あらゆる 64 ビット OS — Linux(glibc 2.34+ は 32 ビットハードウェアも修正)、macOS、Windows
- Python — タイムスタンプは 64 ビット浮動小数点を使い、西暦 2 億 9200 万年をはるかに超えて安全
- JavaScript — Date は 64 ビット IEEE 754 浮動小数点を使い、西暦 275,760 年まで安全
- Go と Rust — 内部で時刻に int64 を使い、何十億年も安全
- Java — java.time.Instant は long(64 ビット)を使い、西暦約 2 億 9200 万年まで安全
- PostgreSQL TIMESTAMP — 2038 年以降の日付も正しく保存
- MySQL DATETIME — 範囲は 1000-01-01 〜 9999-12-31、影響なし
MySQL TIMESTAMP と 2038 年の上限
MySQL には主要な日時列型が 2 つあり、2038 年付近で大きく異なる挙動をします。
- TIMESTAMP は内部で符号付き 32 ビット Unix 整数として保存される — 2038-01-19 03:14:07 UTC にオーバーフロー
- DATETIME はパックされた Y/M/D h/m/s フィールドとして保存され、西暦 9999 年まで対応 — 2038 年以降になり得る日付に使う
- TIMESTAMP はサーバーのセッションタイムゾーンへ暗黙に変換もする。DATETIME は値をそのまま保存
- 生のエポック列には INT ではなく BIGINT を保存し、値自体がオーバーフローしないようにする
- 既存スキーマを監査:SELECT TABLE_NAME, COLUMN_NAME FROM information_schema.COLUMNS WHERE DATA_TYPE = "timestamp"
組み込み Linux と 32 ビット time_t
残る最大の Y2038 リスクは長寿命の組み込みシステムにあります。ルーター、産業用コントローラー、車載 ECU、スマートメーター、そして 32 ビットカーネルで出荷され 2038 年以降も稼働し続けうる IoT 機器です。C ライブラリと独自のバイナリプロトコルの両方が 64 ビットクリーンである必要があります。
- glibc はビルドマクロ -D_TIME_BITS=64 で 32 ビット ABI 向けに 64 ビット time_t を獲得(glibc 2.34+)
- musl libc はバージョン 1.2.0(2020 年)から既定で 64 ビット time_t
- Debian、Ubuntu、OpenWrt は 32 ビットポートをすべて 64 ビット time_t へ移行済み
- ファイルシステムのタイムスタンプは別問題:ext2/ext3 は 32 ビット時刻、ext4 は 256 バイト以上の inode で 64 ビット対応
- 2026 年以降もまだ出荷されるハードウェアでは、C ライブラリと任意のワイヤ形式の両方が 64 ビットであることを確認する
Y2038 の修正とテスト方法
修正は常に同じ — 時刻を 64 ビットに広げる — ですが、タイムスタンプを保存または送信するすべての層に適用する必要があります。C ライブラリ、データベース列、そして任意のバイナリプロトコルやファイル形式です。問題を見つける最速の方法は、テスト環境で時計を境界の先へ進め、1901 年に着地する日付を監視することです。
- glibc 2.34+ で 32 ビットコードを -D_TIME_BITS=64(および -D_FILE_OFFSET_BITS=64)でコンパイルする
- 生の Unix 秒またはミリ秒を保存する列には INT ではなく BIGINT を使う
- 2038 年を超え得る日付には MySQL TIMESTAMP より DATETIME か TIMESTAMPTZ を優先する
- テストでは、システムまたはコンテナの時計を 2038-01-19T03:14:08Z に設定し、日付が 2038 年に留まることを確認する
- ワイヤ形式やファイルヘッダーを確認する — プロトコルのフィールドが 32 ビットなら 64 ビット OS でも助からない
関連するロールオーバー:Y2K、GPS、NTP、2106 年
2038 年は固定幅ロールオーバーの一族の 1 つです。いずれも時計を少なすぎるビットに詰め込むことから生じ、日付を知っておくと出荷前に次のものを見つける助けになります。
| ロールオーバー | 日付 | 原因 |
|---|---|---|
| Y2K | 2000-01-01 | 年を 2 桁で保存 |
| GPS 週ロールオーバー | 2019-04-06 | 10 ビットの GPS 週カウンター |
| NTP エポックのロールオーバー | 2036-02-07 | 1900 年以降の符号なし 32 ビット秒 |
| 2038 年(Y2K38) | 2038-01-19 | 1970 年以降の符号付き 32 ビット秒 |
| 2106 年 | 2106-02-07 | 1970 年以降の符号なし 32 ビット秒 |
関連リファレンス
FAQ
- 2038 年問題とは何ですか?
- 2038 年問題(Y2K38)は、Unix タイムスタンプを符号付き 32 ビット整数で保存するシステムが 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 年のオーバーフローが起きる正確な瞬間です。
- 2038 年問題はどう修正しますか?
- 時刻を 32 ビットではなく符号付き 64 ビット整数で保存します。64 ビット time_t を使い(glibc 2.34+ では 32 ビットコードを -D_TIME_BITS=64 でビルド)、エポック列には INT ではなく BIGINT、MySQL TIMESTAMP ではなく DATETIME を使います。符号付き 64 ビットの時刻値は約 2920 億年オーバーフローしません。
- Y2K38 は Y2K と同じですか?
- いいえ。Y2K(2000 年問題)はプログラムが年を 2 桁で保存したことが原因でした。Y2K38 は Unix タイムスタンプを符号付き 32 ビット整数で保存し 2038 年にオーバーフローすることが原因です。原因も影響を受けるシステムも異なります。
- 2106 年問題とは何ですか?
- 2106 年は符号なし 32 ビットタイムスタンプの同じオーバーフローで、2106 年 2 月 7 日 06:28:15 UTC に尽きます。符号付きから符号なし 32 ビットへ切り替えると 68 年延びますが、真の解決ではありません。64 ビット時刻こそが解決です。
- 2038 年にインターネットは崩壊しますか?
- おそらく大きくは崩壊しません。インターネットインフラの大半はすでに 64 ビットシステムへ移行済みです。リスクは組み込みシステム、古い IoT 機器、何十年も更新されていないソフトウェアに集中しています。現代の OS、プログラミング言語、データベースはすでに安全です。