Unixタイムスタンプ・タイムゾーン・ISO 8601分散システム時刻完全攻略
Unix Epochの定義と2038年問題(Y2038)、秒とミリ秒の混同バグ、サマータイム(DST)の落とし穴、データベースでのUTC保管ベストプラクティスを解説します。
Unixタイムスタンプ変換
データをサーバーに送信せず、すべてブラウザ内で安全に検証・実行できます。
Unix Epoch時間とは何か、なぜ世界標準になったのか
Unixタイムスタンプ(POSIX時間)は、Unix Epoch(協定世界時 1970年1月1日 木曜日 00:00:00 UTC)からの経過秒数を表す符号付き整数値です。各国のタイムゾーン(時差)やサマータイム(夏時間 / DST)による時計の進み戻りといった人間社会の複雑な時計都合を完全に排除し、単一のスカラー数値のみで世界共通の絶対的な前後関係を比較・ソートできることから、すべてのコンピューターアーキテクチャの標準規格として君臨しています。
最も多発する秒(Seconds)とミリ秒(Milliseconds)の混同事故
多言語が混在するマイクロサービス環境で最も頻繁に発生する深刻なバグが、タイムスタンプの「単位の不一致」です:
- Unixタイムスタンプ(秒単位): C言語、Linuxカーネル、Pythonの
time.time()、Go言語のtime.Now().Unix()における業界標準です(10桁の整数、例:1791000000)。 - JavaScriptやJavaのタイムスタンプ(ミリ秒単位): JavaScriptの
Date.now()やJavaのSystem.currentTimeMillis()は、ミリ秒単位(13桁の整数)を返します。
もしバックエンドAPIから渡された10桁の秒数タイムスタンプを、JavaScriptのnew Date(timestamp)にそのまま渡すと、ブラウザはそれをミリ秒として解釈し、1970年1月21日の誤った日時に換算されてしまいます。システム境界での単位変換規約をチーム内で徹底しましょう。
// バックエンドAPIから取得した10桁のUnixタイムスタンプ(秒単位)
const unixSeconds = 1791000000;
// ❌ 誤り:そのまま渡すとミリ秒と誤認され1970年1月に化ける
const badDate = new Date(unixSeconds);
console.log(badDate.toISOString()); // 出力: 1970-01-21T17:50:00.000Z
// ✅ 正解:1000を掛けてミリ秒単位に補正してからDateオブジェクトを生成
const correctDate = new Date(unixSeconds * 1000);
console.log(correctDate.toISOString()); // 正しい未来の日時が出力される2038年問題(Year 2038 Problem, Y2038)の本質と現状
歴史的なシステムで時間を符号付き32ビット整数(time_t)として保持している場合、表現可能な最大数値は2,147,483,647です。
この上限に達する瞬間である2038年1月19日 火曜日 03:14:07 UTCを迎えると、整数オーバーフローが発生して符号ビットが反転し、時刻が一転して1901年12月13日へと逆戻りしてしまいます。現代の64ビットOS、64ビットデータベース、およびNode.jsやPythonなどの主要言語ランタイムではすでに64ビット整数への移行が完了しており、約2,920億年先までオーバーフローすることなく安全に時刻を扱えるようになっています。
分散Webアプリケーションで時間を正しく扱うための鉄則
- データベースには常にUTC基準で保存する: PostgreSQLの
TIMESTAMP WITH TIME ZONE(TIMESTAMPTZ)を使用し、内部的には常にUTCとして記録します。 - API通信のフォーマットはISO 8601(RFC 3339)に統一する:
"2026-10-03T14:30:00.000Z"のようなタイムゾーンオフセットを含む標準文字列で受け渡しを行います。 - 画面表示の直前にのみクライアントのローカル時間に変換する: フロントエンドでブラウザの
Intl.DateTimeFormatAPIを用いて、閲覧ユーザーの端末設定に合わせた地域時刻にフォーマットします。 - 時差のオフセット(+09:00など)をコードにハードコードしない: 国や地域によってサマータイムの開始日や法改正でオフセットが変更されるため、常にIANA Time Zoneデータベース(例:
Asia/Tokyo,America/New_York)の識別子で管理してください。
サマータイム(DST)の針戻りとうるう秒(Leap Second)の分散処理
分散スケジューラーや定期実行バッチ(Cron)は、夏時間(サマータイム / DST)の切り替え時に深刻な不具合を起こすリスクがあります:
- Spring Forward(1時間のスキップ): 時計が01:59:59から03:00:00へ1時間進む際、02:30:00に予定されていたタスクはトリガーされずにスキップされる事故が起こります。
- Fall Back(1時間の重複): 時計が01:00:00へ1時間戻る際、01:30:00のバッチが二重に実行され、請求の重複処理や二重エクスポートが発生する恐れがあります。
- アーキテクチャ上の鉄則: すべての分散ジョブスケジューラー(Temporal, BullMQ, Celeryなど)は、常にローカル時刻ではなく絶対的なUTC時間を基準に稼働させてください。
- うるう秒スミアリング(Leap Smearing): うるう秒(23:59:60)によるシステム時計の停止や逆行を防ぐため、Google、Cloudflare、AWSなどは24時間にわたってミリ秒単位で微小に時計を伸ばしてうるう秒を吸収するLeap Smearingを採用しています。
// サマータイムの針の進退に影響されず、2時点間の真の経過時間(時間単位)を算出
function getTrueElapsedHours(startIso, endIso) {
const startMs = new Date(startIso).getTime(); // 単調増加するUTCミリ秒に変換
const endMs = new Date(endIso).getTime();
return (endMs - startMs) / (1000 * 60 * 60); // 地域時刻の切り替えに左右されない
}
// ロンドンの夏時間開始日: 2026-03-29 01:00 UTC -> 02:00 BST(現地日は23時間だがUTC経過時間は正確に24時間)
console.log(getTrueElapsedHours("2026-03-29T00:00:00Z", "2026-03-30T00:00:00Z")); // 24Unixタイムスタンプ変換
Unix EpochタイムスタンプとUTC・ローカル日時を簡単に相互変換。