UNIX タイムスタンプ完全ガイド:1970 年から現在まで
1738368000 という数字を例にしよう。規則を知っていれば、これは 2025 年 2 月 1 日 00:00:00 UTC だと一目で読める。知らなければ、ただのノイズだ。
これが Unix 時間の設計そのものだ。一つの整数、タイムゾーンなし、暦なし、夏時間なし、「どの 2 月か」を争う必要もなし。 この記事の残りはすべて、この一文の帰結である——その整数がいつか尽きるという話も含めて。
I. 一つの整数、二つの読み方
定義は一文で終わる。ただし、その一文のどの語にも重みがある。
Unix 時間とは、1970 年 1 月 1 日 00:00:00 UTC から経過した秒数であり、うるう秒は数えない。
この一文には四つのことが詰まっている。
UTC の固定された瞬間から数える。 あなたの地域の深夜零時からではない。東京とシカゴの二人が同じタイムスタンプを見れば同じ数字を見て、それぞれ自分の壁時計に換算する。
単位は秒。 初期の Unix は 60 分の 1 秒で数えていた。それは次の節の話だが、数字がこれほど小さいのはそのためだ。
うるう秒は除外される。 ここがほぼ全員が間違える点で、独立した節を設けてある。
負の値もありうる。 1970 年より前の日付は単なる負数であって、エラー状態ではない。-1 は 1969-12-31 23:59:59 UTC だ。
同じ瞬間を六通りに書いてみる。タイムゾーンを持つのは後ろ三つだけで、コンピュータが実際に保存するのは最初の二つだけだ。
| 表現 | 値 |
|---|---|
| Unix 秒 | 1738368000 |
| Unix ミリ秒 | 1738368000000 |
| ISO 8601、UTC | 2025-02-01T00:00:00Z |
| ISO 8601、UTC+8 | 2025-02-01T08:00:00+08:00 |
| ISO 8601、UTC−5 | 2025-01-31T19:00:00-05:00 |
| RFC 2822 | Sat, 1 Feb 2025 00:00:00 +0000 |
タイムスタンプの列は最後まで変わらず、日付の列は三回変わる。タイムスタンプは日付ではない。一本の地球規模の時間軸上の一点だ。 日付とは、「その点が地球上のこのあたりから見てどこに落ちるか」を尋ねたときに返ってくる答えである。
II. なぜ 1970 年で、なぜ 1971 年ではないのか
1969 年に誰かが縁起のいい日を選んで 1970 年 1 月 1 日と決めたわけではない。この起点は二年のあいだに三度書き換えられ、1970 年は最後に偶然そこへ落ち着いた場所にすぎない。
60 Hz という厄介ごと
最初の Unix は PDP-11 で動いていた。この機械のシステムクロックは毎秒 60 回刻む。商用電源が 60 Hz の国のために時を刻む設計だったからだ。だから保存するのは「何回刻んだか」が自然だった。
問題はそのカウンタが 32 ビットしかないことだった。毎秒 60 回なら、32 ビットは一周するのに:
2^32 / 60 秒 = 71,582,788 秒 ≈ 2.26 年
二年三か月。1972 年 3 月 15 日付の第 3 版『Unix プログラマーズマニュアル』は、これを異様に率直に書いている。
"The time is stored in 32 bits. This guarantees a crisis every 2.26 years."
(時刻は 32 ビットに格納される。これにより 2.26 年ごとに危機が保証される。)
二年で三つの起点
手っ取り早い対処は、スタートラインを前にずらし続けることだった。1971 年 11 月 3 日の第 1 版マニュアルはこう定義している。
"the time since 00:00:00, Jan. 1, 1971, measured in sixtieths of a second"
(1971 年 1 月 1 日 00:00:00 からの 60 分の 1 秒数。)
四か月後の第 3 版では、起点はすでに 1972 年 1 月 1 日に移っている。半年足らずで二つの起点——カウンタが尽きる速さのほうが、文書を書く速さより速かったのだ。
本当の解決は「刻み」を数えるのをやめ、「秒」を数えることにした。これで余裕は六十倍になる。32 ビットは 2.26 年から約 136 年に伸びた。単位が変われば起点を最近にする必要はなくなり、1970 年 1 月 1 日に決まった——マニュアル自身の言い方によれば「扱いやすい日付」として、任意に選ばれたのだ。
なぜ二度と動かなかったのか
1970 年代半ばには、すでにディスクの上にデータがあったからだ。ファイル、テープ、inode、バックアップアーカイブ——どれもが「1970 年からの秒数」を抱えていた。起点をもう一度変えることは、世界中の Unix マシンのあらゆるファイルを、黙って別の意味に読み替えることに等しかった。
こうして偶然の選択はインターフェースへと固まった。五十六年たった今も、地球上のすべてのスマートフォン、サーバー、データベースが、あの木曜の朝から数え続けている。

PDP-11/20。Unix が生まれたマシン系列。パネルには「Digital Equipment Corporation (DEC), US, 1970」とあり、起点と同じ年である。画像:Don DeBold、CC BY 2.0、Wikimedia Commons より
III. 毎日はちょうど 86,400 秒
ここから Unix 時間は物理世界と一致しなくなる。
地球の自転は一様ではない。遅くなり、少し速くなり、揺れる。天文学者は地球の実際の向きから導かれる UT1 という時間尺度でこれを追い、原子時計はそんなことに関知しない。二つの尺度は少しずつ離れていく。
1972 年に発明された継ぎ当てがうるう秒だ。6 月 30 日か 12 月 31 日の最後の一分に 23:59:60 と表示される秒を挿入し、原子時と天文時を 0.9 秒以内に保つ。これまでに 27 回挿入され、直近は 2016 年末である。
Unix 時間はそれをすべて無視する。POSIX 規格は事実上、毎日はちょうど 86,400 秒であり例外はない、と定めている。したがってこの数字は「物理的に何秒経過したか」ではなく、実時間の上に敷かれた理想化された完全に均一な格子の上の座標だ。
その代償
二つあり、どちらも目立たない。
曖昧さ。 正のうるう秒が起きると、一つの Unix 値を使い回さなければならない。1998 年末、915148800 は 1998-12-31 23:59:60 と 1999-01-01 00:00:00 の両方を指した。2016 年末の 1483228800 も同じである。この一秒のあいだ、タイムスタンプは一意な瞬間を指さない。
誰も使わない並行定義。 一部の Linux は right 設定で動いており、こちらは本当にうるう秒を数えて一日を 86,401 秒にする。1972 年の最初のうるう秒以来、right 時刻は通常の posix 時刻より進んでおり、2017 年時点でちょうど 27 秒先行している。二台のマシンを比べて 27 秒のずれを見たことがあるなら、原因はこれだ。
この仕組みは廃止される
2022 年 11 月、第 27 回国際度量衡総会はうるう秒の廃止を決め、目標を 2035 年またはそれ以前とした。そして 2026 年 10 月、第 28 回総会が期日を確定した——2027 年 5 月 20 日から UTC は連続運転になる。理由は揺らぎが止まったからではなく、揺らぎがエンジニアリング上の負債になったからだ。うるう秒はルーターを落とし、取引システムを混乱させ、十年に数回しか走らないがゆえに十分なテストができない特例コードを、あらゆる大企業に書かせてきた。
以後 UTC は地球の自転からさらに離れることを許され、Unix 時間は初めて、本当に一様な秒数の計数になる。
実務のルール
二つのタイムスタンプを引いて所要時間を測ってはいけない。
end - start は、あいだにうるう秒が挟まれば誤りであり、NTP やユーザーによってシステム時計が調整されても誤りになる。経過時間を測るには単調クロックを使う——C の CLOCK_MONOTONIC、ブラウザの performance.now()、Python の time.monotonic()。これらは実際の経過を数え、二つの問題のどちらにも影響されない。タイムスタンプは瞬間をラベル付けするためのもので、単調クロックは区間を測るためのものだ。混同すると、年に一度しか現れず再現できないバグが生まれる。
IV. 32 ビットの借金
カウンタの話に戻る。
符号付き 32 ビット整数の範囲は −2,147,483,648 から 2,147,483,647 まで。これを「1970 年からの秒数」に当てはめると、固定された窓ができる。
| 境界 | 値 | 日付 |
|---|---|---|
| 最小値 | -2147483648 | 1901-12-13 20:45:52 UTC |
| 起点 | 0 | 1970-01-01 00:00:00 UTC |
| 最大値 | 2147483647 | 2038-01-19 03:14:07 UTC |
最大値の一秒後、値はあふれて最小値に巻き戻る。この格納方式のシステムは、2038 年 1 月 19 日 03:14:08 UTC の瞬間に、いまが 1901 年 12 月 13 日だと思い込む。

int32 は 1901 年から 2038 年、符号なし版は 1970 年から 2106 年を覆う。塗りつぶした部分がすでに使った割合だ。
どこまで来ているか
int32 の窓は全長 136.1 年で、2026 年はちょうど 91.7% の位置にある。ここが最も過小評価される点だ。2038 年問題は遠い締め切りではなく、九割まで埋まったプログレスバーなのである。
符号なし版はさらに 68 年を稼ぎ、2106-02-07 06:28:15 UTC まで届く。図の uint32 の行はそこから来ており、一部の形式が「二度壊れる」理由でもある。
実際に壊れるのはどこか
目に見える被害の大半は OS の中では起きない。Linux はずっと前に 64 ビット time_t へ移行し、主要ソフトの 64 ビットビルドも追随した。リスクは変えにくい場所に集中する。
- データベース。 MySQL の
TIMESTAMP列は符号付き 32 ビットの秒数そのもので、2038-01-19 03:14:07 で止まる。その場で拡張する方法はない。DATETIMEは範囲がはるかに広く、2038 年を超えて生きる必要のある列には正しい選択だ——ただし移行は手作業で、すべてのテーブルを調べる必要がある。 - ファイル形式。 アーカイブ、画像メタデータ、メールストア、バックアップカタログの 32 ビットタイムスタンプ欄は、それを読む機械が入れ替わったずっと後まで、巻き戻った日付を書き続ける。
- 組み込みシステム。 産業用コントローラ、医療機器、カーナビ、ビル管理。二十年の運用寿命があり、更新経路がなく、製造元がもう存在しないのはこういう類だ。
- 32 ビットのユーザー空間。 古い ARM ボードや、古いベースイメージから作ったコンテナで今も一般的だ。

サーバーラック。2038 年に実際に壊れるシステムは、たいていこれらより古く、目立たない。画像:Carl Lender、CC BY 2.0、Wikimedia Commons より
なぜ 2000 年問題より厄介なのか
2000 年問題は書式の問題だった。期限は明確で、試験方法も明確——時計を進めて、日付が正しく見えるか確かめればよかった。
2038 年は符号付き算術の問題だ。あふれてもエラーは出ない。1901 年の正しい日付になり、そこから伝播する。証明書は 124 年前に失効したように見え、セッショントークンは古色を帯び、並び順の比較が黙って反転する。
故障は静かで部分的になる。それが最悪の組み合わせだ。
V. 秒では足りなくなった
コンピュータの最速の処理がミリ秒かかっていた頃は、秒で足りた。今は足りない。
| 精度 | 典型的な格納 | どこで出会うか |
|---|---|---|
| 1 秒 | time_t | ファイルシステム、データベース、HTTP ヘッダ |
| 1 ミリ秒 | JavaScript Date、Java Instant | ブラウザ、JSON API、Date.now() |
| 1 マイクロ秒 | struct timeval | gettimeofday、古めの C API |
| 1 ナノ秒 | struct timespec | clock_gettime、APFS、ext4、現代のカーネル |
これはタイムスタンプを扱うコードで最も多いバグ源だ。1738368000 と 1738368000000 は同じ瞬間の二つの単位で、どちらも妥当な整数に見える。千倍という係数は、逆方向に掛けるのは簡単で、目で見つけるのはほぼ不可能だ。
JavaScript の天井
JavaScript の Date はミリ秒を倍精度浮動小数で持つ。仕様は ±8.64 × 10¹⁵ ミリ秒、すなわち ±1 億日、起点の前後およそ ±273,790 年に制限している。int32 の窓よりはるかに広く、JavaScript に 2038 年問題は存在しない。しかしミリ秒という単位のせいで、多くの API が十三桁の数を返し、Date.now() / 1000 という行がこれほど見慣れたものになっている。
時間を内蔵する識別子
現代のシステムはタイムスタンプを ID 自体に埋め込むことが増え、精度の選択は実装の詳細から設計判断へと変わった。UUIDv7 は先頭に 48 ビットのミリ秒を置き、1970 年から約 8,900 年を覆い、ID を作成時刻順に自然に並べられるようにする。Snowflake 系の ID も 41 ビットのミリ秒で同じことをし、その代償として範囲は数十年になる。
名指ししておきたい落とし穴
タイムスタンプを float32 に格納してはいけない。ミリ秒の桁では float64 にも注意が要る。32 ビット浮動小数は仮数部が 24 ビットしかなく、現在のタイムスタンプの桁では約 128 秒の刻みしか区別できない。値は間違いではなく丸められている——こちらのほうが厄介で、一分差の二つの事象が同じタイムスタンプになるまで、すべて正常に見える。
VI. 実務で壊れる七つのパターン
実際にバグトラッカーに現れるものを、おおよそ頻度順に並べる。
1. 秒かミリ秒か。 数字が届いても桁だけでは判断できず、選び間違えれば日付は 1970 年か西暦 57,000 年になる。簡易チェック:現在のタイムスタンプは秒で約 10 桁、ミリ秒で 13 桁。
2. ローカル時刻はタイムスタンプではない。 date -d @0 は 1970-01-01 00:00:00 を表示しない。UTC の零時をあなたのタイムゾーンで表した値を出す——日本なら 1970-01-01 09:00:00、ニューヨークなら 1969-12-31 19:00:00。数字は同じで、表示が局所的なのだ。
3. タイムスタンプにタイムゾーンは入っていない。 これは Unix 時間の欠陥ではなく、目的そのものだ。ただし、タイムゾーンなしの「2025-02-01 00:00:00」は瞬間を成さず、そんな文字列を保存するシステムは、遅かれ早かれ別のシステムとその意味をめぐって食い違う。
4. タイムスタンプを文字列で並べると桁の境界で壊れる。 2001 年 9 月 9 日に 1000000000 が来たとき、タイムスタンプを数値ではなく文字列で比較していたソフトは、新しい項目を古い項目の前に並べ始めた——"999999999" は "1000000000" より後ろにソートされるからだ。ゼロ埋めか整数格納で解決する。他の方法はない。
5. MySQL の TIMESTAMP。 前述のとおりだが、普通の Web アプリで最も多い 2038 年リスクなので独立して挙げておく。
6. 1970 年より前。 負の値は合法で、「タイムスタンプ=正の整数」を前提にしたバリデータ、データベース列型、API スキーマにしばしば拒否される。生年月日、歴史記録、天文データはすべて起点の反対側に住んでいる。
7. 壁時計を引く。 これも前述だが、間欠的で再現不能な本番障害を生むのはこの書き方なので再掲する。所要時間には単調クロックを使うこと。
VII. 人が祝うタイムスタンプ
Unix 時間にはささやかな「整数好き」の文化がある。以下はよく話題にされるものだ。
| タイムスタンプ | 日付(UTC) | 何が起きたか |
|---|---|---|
0 | 1970-01-01 00:00:00 | 起点そのもの |
-1 | 1969-12-31 23:59:59 | 範囲が過去へ伸びることの証明 |
119731017 | 1973-10-17 18:36:57 | 自己記述的な参照として使われた最初のタイムスタンプ |
1000000000 | 2001-09-09 01:46:40 | 最初の十桁。文字列ソートがここで破綻する |
1073741824 | 2004-01-10 13:37:04 | 2³⁰、いわゆる「1337」タイムスタンプ |
1234567890 | 2009-02-13 23:31:30 | Google が doodle で祝った |
2147483647 | 2038-01-19 03:14:07 | 表現できる最後の秒 |
4294967295 | 2106-02-07 06:28:15 | 符号なし版の天井 |
2001 年の事例が興味深いのは、2038 年の縮図だからだ。完全に妥当な数で、どのシステムも受け入れ、そして比較が静かに壊れた。
VIII. 2038 年より後
解決は複雑ではない。符号付き 64 ビットの time_t は前後およそ 2,920 億年を覆う——宇宙の年齢のおよそ二十倍だ。その先に第二の 2038 年問題はなく、現実のソフトウェアがこの上限を意識する必要は二度と来ない。
現在の 64 ビットプラットフォームはほぼすべて移行済みだ。残る作業はカーネルの中ではなく、データベース、ファイル形式、ファームウェア、そして二十年動き続けている機器の中にある。
もう二つの折り返し日
知っておく価値のある日付がもう二つある。有名ではないが、どちらも現実だ。
2036-02-07 06:28:16 UTC — NTP 第 0 紀元の終わり。 Network Time Protocol は 1900 年 1 月 1 日から符号なし 32 ビットで秒を数える。2036 年に尽き、Unix の場合と違ってパケット形式を壊さずに桁を広げる余地がない。プロトコルには拡張機構が定義されているが、両端の更新を要する——NTP においてそれは、時刻同期を設定されたことのあるすべての機器を意味する。
2106-02-07 — 符号なし 32 ビットの天井。 秒を int32 ではなく uint32 で持った形式は 68 年余分に得る。一部のファイル形式や組み込みプロトコルがまさにそうしており、図に棒が二本あるのはそのためだ。
何をすべきか
2038 年にも動いているシステムを保守しているなら:
- タイムスタンプを固定幅整数で保存している箇所と、
TIMESTAMP型の列をすべて洗い出す。 - 長寿命のデータには
DATETIME、64 ビット整数、ISO 8601 文字列を優先する。 - システム時計を 2038-01-19 より先に進め、読み書きの全経路を通すテストを書く。安価なテストだが、ほとんど誰も持っていない。
- 文字列によるタイムスタンプ比較は、スタイルの好みではなく潜在的なバグとして扱う。
結び:1970 年は一つの決定である
1738368000 という数字は、宇宙についての事実のように見える。そうではない。1971 年に刻みを数めると決め、1972 年に秒を数えると決め、「二年ごとに起点をずらす」運用が成り立たなくなった後に適当なスタートラインを引いた、その重なりの結果だ。
五十六年後、その選択はあらゆるファイルシステム、あらゆるデータベース、あらゆるログ行、そしてインターネット上のあらゆる API に埋め込まれている。それは決めた人より長く生き、生まれた機械より長く生き、おそらく同じ根の問題を塞ぐために発明されたうるう秒よりも長く生きるだろう。
そこには少しだけ、視界が晴れる感じがある。タイムスタンプは時間ではない。約束事だ——有用なほど精密で、一つの整数に収まるほど単純な、非常によくできた約束事であり、他のあらゆる約束事と同じく、来歴と賞味期限と維持コストを持っている。
この数字がどこから来たかを知っていれば、その値が秒かミリ秒か、その日付が UTC かローカルか、そして目の前のシステムが 2038 年に無事かどうかを、一目で判断できる。
特定の値を調べたいときは、タイムスタンプ変換ツール が秒・ミリ秒・ISO 8601 の双方向変換に対応している。タイムゾーン処理もあり、数字はあるが単位が分からないときに便利だ。