本文へ移動
ZEKILO Dev
変換

UNIX時間の秒・ミリ秒の見分け方と2038年問題

2026.10.03 更新 · 6分で読めます

UNIX時間は1970年から数えた秒数で、タイムゾーンを持ちません。桁数で秒とミリ秒を見分ける方法、日本時間に変換するときの落とし穴、32ビット整数があふれる2038年問題と確認すべき箇所を解説します。

UNIX時間変換をすぐに使う

UNIX時間は1970-01-01T00:00:00Zから数えた秒数で、タイムゾーンを持ちません。現在の日時なら10桁は秒、13桁はミリ秒で、この2つの取り違えがタイムスタンプで最も多い不具合です。2038年問題は秒を符号付き32ビット整数に入れている箇所だけに関係し、その上限は2038-01-19T03:14:07Zです。値をUNIX時間変換に貼り付けると、どの単位として読んだかと、UTC・日本時間の日時をすぐに確認できます。

秒・ミリ秒・マイクロ秒・ナノ秒

同じ瞬間(2026-01-01T00:00:00Z)を4つの単位で書くと次のようになります。

単位 値 桁数 見かける場所
秒 1767225600 10 POSIXのtime()、JWTのexp・iat
ミリ秒 1767225600000 13 JavaScriptのDate.now()
マイクロ秒 1767225600000000 16 マイクロ秒精度のログやデータベース
ナノ秒 1767225600000000000 19 トレーシング、高分解能の時計

秒単位の値は2001-09-09T01:46:40Zから2286-11-20T17:46:39Zまで10桁なので、桁数で見分けられます。ツールの自動判定も同じ考え方です。絶対値が10^11より小さければ秒、10^14より小さければミリ秒、10^17より小さければマイクロ秒、それ以上はナノ秒として読み、どの単位と解釈したかをバッジで示します。そのため1767225600123はミリ秒と判定され、2026-01-01T00:00:00.123Zになります。

単位を取り違えると、見分けやすい結果になります。

起きたこと 結果
秒の1767225600をミリ秒として読んだ 1970-01-21T10:53:45.600Z
ミリ秒の1767225600000を秒として読んだ 西暦57971年の日付

日付が1970年1月になるときは、ミリ秒を受け取る箇所に秒を渡していることがほとんどです。JavaScriptではnew Date(1767225600)が1970年1月になり、new Date(1767225600 * 1000)が意図した日付です。逆に、RFC 7519はJWTのexpとiatを秒と定義しているので、Date.now()ではなくMath.floor(Date.now() / 1000)と比較します。これらのクレームはJWTデコーダーで日時として確認できます。

ナノ秒の値は、JavaScriptではもう1つ注意が必要です。Numberが抜けなく正確に表せる整数(安全な整数)は9,007,199,254,740,991までで、上のナノ秒の値はそれより大きくなります。Number('1767225600000000001')は1767225600000000000になり、最後の桁が失われます。文字列かBigIntで扱ってください。

UNIX時間にタイムゾーンはありません

POSIXは基準時点(Epoch)を1970-01-01 00:00:00 UTCと定義しています。そのため1つのタイムスタンプは世界中どこでも同じ数値で、タイムゾーンが関わるのは人が読む日時に変換するときだけです。

1767225600  =  2026-01-01T00:00:00Z     UTC
            =  2026-01-01 09:00:00      日本時間(JST、UTC+9)

逆に、2026-01-01 09:00:00をAsia/Tokyoのタイムゾーンで変換すると1767225600に戻ります。

よくある落とし穴は、オフセットのない日時文字列です。ECMA-262は、UTCオフセットがない場合、日付だけの形式はUTC、日付と時刻のある形式はローカル時刻として解釈すると定めています。タイムゾーンがUTC+9のマシンで実行すると次のようになります。

Date.parse('2026-01-01')             →  1767225600000   UTCとして解釈
Date.parse('2026-01-01T00:00:00')    →  1767193200000   ローカル時刻として解釈(9時間早い時点)
Date.parse('2026-01-01T00:00:00Z')   →  1767225600000   UTCを明示

同じコードでも、UTCに設定されたサーバーでは別の数値になります。文字列には必ずZか+09:00のようなオフセットを付けてください。RFC 3339の形式はオフセットを必須としています。

定義から導かれる性質がもう1つあります。POSIXは1日をちょうど86,400秒として数えると定めているため、UNIX時間はうるう秒を数えません。ECMAScriptも同じ方式です。

2038年問題

符号付き32ビット整数の最大値は2,147,483,647です。これを秒として読むと次のようになります。

 2147483647  →  2038-01-19T03:14:07Z   (日本時間 12:14:07)
-2147483648  →  1901-12-13T20:45:52Z   1秒後に値があふれて戻る先

秒を符号付き32ビットに入れているソフトウェアは、その瞬間に1901年へ戻ったりエラーになったりするおそれがあります。影響を受けるかどうかは、値をどこに入れているかで決まります。

保存方法 表せる最後の時刻
符号付き32ビットの秒 2038-01-19T03:14:07Z
符号なし32ビットの秒 2106-02-07T06:28:15Z
符号付き64ビットの秒 1970年から約2,920億年
JavaScriptのDate(ミリ秒) 275760-09-13T00:00:00Z(±8,640,000,000,000,000 ms)
MySQLのTIMESTAMP列(MySQL 8.4マニュアル) ‘2038-01-19 03:14:07’ UTC

問題は2038年になってから始まるわけではありません。上限を超える将来の日時を計算するコードは、今でも影響を受けます。1767225600に365日×15年分の秒数を足すと2240265600(2040-12-28T00:00:00Z)になり、符号付き32ビットに収まりません。有効期限、契約期間、証明書の期限が典型例です。

確認したい箇所は次のとおりです。

  • 秒を入れている整数の列: 符号付き32ビットのINTは上限が同じです。64ビットの整数型を使ってください。
  • MySQLのTIMESTAMP: マニュアルによると範囲は’1970-01-01 00:00:01’ UTCから’2038-01-19 03:14:07’ UTCまで、DATETIMEは’1000-01-01 00:00:00’から’9999-12-31 23:59:59’までです。TIMESTAMPは保存時にUTCへ変換し、DATETIMEは変換しないため、型を変えるとタイムゾーンの扱いも変わります。
  • 32ビットのビルドやバイナリ形式: 32ビットのtime_t、時刻フィールドが32ビット固定のファイル形式やプロトコル。

ZEKILO Devで変換する

  1. UNIX時間変換に数値を貼り付けます。単位は自動で判定され、秒・ミリ秒・マイクロ秒・ナノ秒を自分で指定することもできます。
  2. 結果はISO 8601(UTC)、RFC 2822、日本語の日時表記、相対時間で表示されます。ブラウザのタイムゾーンとUTC・KST・JSTが並んで表示され、ほかのタイムゾーンはIANA名で選べます。
  3. 日時からタイムスタンプに変換するときは、ISO 8601、YYYY-MM-DD HH:mm:ss(選んだタイムゾーンを適用)、RFC 2822の形式で入力します。

2147483647以降の値には、32ビットのtime_tの上限に関する案内が表示されます。計算はすべてブラウザ内で行います。

まとめ

  • UNIX時間は1970-01-01T00:00:00Zから数えた秒数です。タイムゾーンを持たず、うるう秒を数えません。
  • 現在の日時なら、10桁は秒、13桁はミリ秒、16桁はマイクロ秒、19桁はナノ秒です。
  • 日付が1970年1月になったら、秒をミリ秒として読んでいます。
  • 保存はUTC基準の64ビット整数かZ付きの文字列で行い、日本時間への変換は表示のときだけにします。
  • 符号付き32ビットの秒は2038-01-19T03:14:07Zで終わります。INTとMySQLのTIMESTAMP列を確認してください。

この記事に関連するツール

出典と基準

ほかのガイド