Unixタイムスタンプとタイムゾーンの基本:日時が9時間ずれる原因と防ぎ方
公開日:
ログに残っている1790175600という数字が何時なのか分からない、データベースに保存した日時が9時間ずれている、夜中に動くはずのバッチが朝9時に動いた――日時を扱う開発では、Unixタイムスタンプとタイムゾーンの理解不足によるトラブルがよく起きます。この記事では、仕組みと典型的な落とし穴、防ぎ方をまとめます。
Unixタイムスタンプとは
Unixタイムスタンプ(Unix時間、エポック秒)は、1970年1月1日0時0分0秒(UTC)からの経過秒数で日時を表す方法です。例えば1790175600は、UTCで2026年9月23日15時0分、日本時間(JST)で2026年9月24日0時0分を表します。
タイムスタンプには、次のような特徴があります。
- タイムゾーンを持たない:世界中どこでも、同じ瞬間は同じ数値になる。「日本時間の数値」「UTCの数値」という区別はない
- 比較や計算がしやすい:ただの整数なので、大小比較や差の計算(経過秒数)が簡単
- うるう秒は数えない:1日は常に86,400秒として扱われる
秒とミリ秒の見分け方
システムによって、秒単位とミリ秒単位の両方が使われます。現在の日時であれば、桁数で見分けられます。
| 単位 | 桁数(現在の日時) | 例 | 主に使われる場所 |
|---|---|---|---|
| 秒 | 10桁 | 1790175600 |
Unix系OS、PHP、Pythonのtime.time()(整数部分)、多くのAPI |
| ミリ秒 | 13桁 | 1790175600000 |
JavaScriptのDate.now()、Java |
単位を取り違えると、日時が大きくずれます。秒の値をミリ秒として扱うと1970年1月の日時に、ミリ秒の値を秒として扱うと数万年後の日時になります。「1970年」や「ありえない未来」が表示されたら、まず単位の取り違えを疑いましょう。Unixタイムスタンプ変換ツールでは、秒・ミリ秒を切り替えて変換結果を確認できます。
タイムゾーンの基本:UTCとJST
UTC(協定世界時)は世界の時刻の基準で、日本標準時(JST)はUTCより9時間進んでいます(UTC+9)。日本にはサマータイムがないため、時差は1年中9時間で固定です。
日時を文字列で表すときは、ISO 8601形式でタイムゾーンを明示すると誤解がありません。
2026-09-24T00:00:00+09:00 日本時間の2026年9月24日0時
2026-09-23T15:00:00Z UTCで表した同じ瞬間(Zは「UTC」の意味)
この2つは表記が違うだけで、同じ瞬間を指しています。
よくある落とし穴
データベースやログの日時が9時間ずれる
サーバーやDockerコンテナは、タイムゾーンの設定がUTCになっていることがよくあります。アプリケーション側は日本時間のつもりで扱い、サーバー側はUTCで記録していると、表示される日時がちょうど9時間ずれます。
「9時間ずれている」ときは、ほぼ確実にUTCとJSTの取り違えです。どこでUTCとして保存され、どこでJSTとして解釈されているかを、保存・読み出し・表示の段階ごとに確認します。
タイムゾーンのない日時文字列の解釈がばらばら
2026-09-24 00:00:00のようにタイムゾーンの情報がない日時は、受け取った側が「どのタイムゾーンとして解釈するか」を勝手に決めます。JavaScriptでは、書き方によって解釈が変わるので特に注意が必要です。
// 日本時間の環境で実行した場合
new Date("2026-09-24"); // 日付のみ → UTCの0時として解釈(日本時間では9時)
new Date("2026-09-24T00:00:00"); // 日付と時刻 → 実行環境のタイムゾーン(日本時間の0時)として解釈
new Date("2026-09-24T00:00:00+09:00"); // タイムゾーン付き → どの環境でも同じ瞬間
日付だけの文字列はUTCとして、時刻付きでタイムゾーンのない文字列は実行環境のタイムゾーンとして解釈されるため、同じ日付でも9時間ずれた結果になります。外部とやり取りする日時には、必ずタイムゾーン(+09:00やZ)を付けるのが確実です。
「今日のデータ」の範囲がずれる
日本時間の0時〜9時は、UTCではまだ前日です。UTCで保存したデータを「日付」で集計すると、日本時間の深夜〜早朝のデータが前日分として数えられてしまいます。日別の集計では、どのタイムゾーンの「1日」で区切るのかを決めてから、その範囲をUTCに変換して検索します。
日本時間の2026年9月24日(1日分)
= 2026-09-23T15:00:00Z 以上 2026-09-24T15:00:00Z 未満
cronやスケジューラーの時刻がずれる
サーバーのタイムゾーンがUTCの場合、cronに0 0 * * *(毎日0時)と書くと、日本時間の朝9時に実行されます。日本時間の0時に実行したい場合は、UTCで0 15 * * *(前日の15時)と書くか、スケジューラー側でタイムゾーンを指定します。cron式の意味はcron式パーサーで確認できます。
2038年問題
Unixタイムスタンプを符号付き32ビット整数で保存しているシステムでは、表せる最大値が2147483647、つまり**2038年1月19日3時14分7秒(UTC)**になります。これを超えると値があふれて、1901年の日時として扱われるなどの不具合が起きます。これが「2038年問題」です。
現在の主要なOSやプログラミング言語の多くは64ビットで扱うため影響を受けませんが、古い組み込み機器やデータベースの列定義(32ビット整数型の列にタイムスタンプを保存している場合など)には残っている可能性があります。長期間使うシステムでは、有効期限や予約日時など未来の日時を保存する列の型を確認しておくと安心です。
日時を安全に扱うための方針
- 保存と通信はUTC(またはUnixタイムスタンプ)で統一し、表示するときに利用者のタイムゾーンへ変換する
- 日時の文字列には必ずタイムゾーンを付ける(ISO 8601の
+09:00やZ) - サーバー・データベース・アプリケーションのタイムゾーン設定をそろえ、ドキュメントに書いておく
- 秒とミリ秒のどちらを使うかを、APIの仕様などで明示する
海外の拠点や利用者とやり取りする場合は、サマータイムのある地域との時差が季節によって変わることにも注意が必要です。特定の日時が各地で何時になるかは、タイムゾーン変換ツールで確認できます。
まとめ
- Unixタイムスタンプは1970年1月1日(UTC)からの経過秒数で、タイムゾーンを持たない
- 10桁なら秒、13桁ならミリ秒。1970年や遠い未来が表示されたら単位の取り違えを疑う
- 9時間のずれは、UTCとJSTの取り違えがほぼ確実な原因
- 日時の文字列にはタイムゾーンを付け、保存はUTC、表示は利用者のタイムゾーンで行う