時間は、それを正しく処理するソフトウェアを書こうとするまでは単純に思えます。「午後3時」に予定されたイベントは、東京、ロンドン、ニューヨークでそれぞれ異なる時刻を意味します。夏時間は時計を前後にずらします――しかし、すべての場所でではなく、同じ日程でもありません。ローカル環境では正しく見えるタイムスタンプが、サーバーが別のタイムゾーンにあるため本番環境で壊れる、ということが起こります。
タイムゾーンは、ソフトウェア開発において最も確実に混乱を招くトピックの1つです。この記事では、タイムゾーンの仕組み、複雑さの原因、そしてよくある間違いを回避する方法を説明します。
タイムゾーンが存在する理由
地球は24時間で360度回転します。つまり、太陽が最も高い位置に来る時刻は経度によって異なります。19世紀以前は、すべての都市が地方太陽時を使っていました。正午は太陽が真上にあるときでした。鉄道が離れた都市を結び、列車の時刻表に統一的な時計が必要になるまで、これで問題ありませんでした。
1884年、25カ国の代表がワシントンD.C.の国際子午線会議に集まり、イギリスのグリニッジを通る本初子午線(経度0度)を基準に、世界を24の標準時間帯に分けることに合意しました。
実際には、タイムゾーンの境界線は整然とした経線ではなく政治的な国境に沿っています。中国は地理的に5つのゾーンにまたがっていますが、単一の公式時間(UTC+8)を使用しています。インドはUTC+5:30という30分単位のオフセットです。ネパールはUTC+5:45です。現実のタイムゾーンの地図は複雑です。
UTC vs. GMT
**GMT(グリニッジ標準時)**は、グリニッジ王立天文台における平均太陽時です。1世紀以上にわたり世界の時刻基準でした。
**UTC(協定世界時)**は、1972年にGMTに代わる国際標準として導入されました。UTCは天文観測ではなく原子時計に基づいており、はるかに正確です。実用的にはUTCとGMTは同じ時刻を示しますが、技術的な基準としてはUTCが正しいものです。
なぜ「CUT」ではなく「UTC」なのか? この略称は、英語の「Coordinated Universal Time」(CUT)とフランス語の「Temps Universel Coordonné」(TUC)の妥協案です。どちらの側も好みの略語を得られなかったため、言語中立的な代替としてUTCが選ばれました。
夏時間:組織化された混乱
約70カ国が**夏時間(DST)**を採用し、春に時計を1時間進め、秋に1時間戻します。目的は活動時間を日照に合わせることです。その結果は、半年ごとのバグの源です。
主な複雑さ:
- 普遍的ではない。 アフリカ、アジア、南米の大部分はDSTを採用していません。米国内でも、アリゾナ州とハワイ州は対象外です。
- 日程が異なる。 EUは3月と10月の最終日曜日に切り替えます。米国は3月の第2日曜日と11月の第1日曜日に切り替えます。毎年数週間は同期が取れていません。
- 曖昧な時刻。 秋に時計を戻すと、午前1時から午前2時の1時間が2回発生します。その日の「午前1時30分」というタイムスタンプは曖昧です。
- スキップされる時刻。 春に時計を進めると、午前2時から午前3時の1時間が存在しなくなります。その日の午前2時30分に予定された会議は実行されません。
- 政治的な変更。 政府はDSTの規則を短い予告で変更できます(し、実際に変更します)。ロシアは2011年に恒久夏時間を採用し、2014年に恒久標準時に切り替えました。モロッコはDSTの規則を何度も変更しています。
ISO 8601:ユニバーサルな日時フォーマット
曖昧さを避けるため、国際標準ISO 8601は明確な日時フォーマットを定義しています:
2026-03-29T14:30:00Z
2026-03-29T14:30:00+02:00
2026-03-29T14:30:00-05:00
Tは日付と時刻を区切ります。ZはUTCを意味します(軍事用語で「Zulu」タイムゾーン)。+02:00や-05:00はUTCオフセットです。
このフォーマットは曖昧さがなく、プレーンテキストとしてソート可能で、日付パースライブラリで普遍的に理解されます。迷ったらISO 8601を使いましょう。
Unixタイムスタンプ
Unixタイムスタンプ(エポック時間またはPOSIX時間とも呼ばれます)は、1970年1月1日 00:00:00 UTC(Unixエポックとして知られる瞬間)から経過した秒数です。
| 人間が読める形式 | Unixタイムスタンプ |
|---|---|
| 1970-01-01 00:00:00 UTC | 0 |
| 2000-01-01 00:00:00 UTC | 946684800 |
| 2026-03-29 12:00:00 UTC | 1774987200 |
Unixタイムスタンプにはタイムゾーンがありません。常にUTCです。そのため、ソフトウェアでの時刻の保存と比較に最適です。ローカルタイムゾーンへの変換は表示レイヤーでのみ行います。
2038年問題: Unixタイムスタンプを32ビット符号付き整数で格納しているシステムは、2038年1月19日03:14:07 UTCにオーバーフローします。最大値(2,147,483,647)が負の数にロールオーバーし、1901年12月と解釈されます。ほとんどの現代のシステムは64ビット整数を使用しており、今後2920億年はオーバーフローしません。
IANAタイムゾーンデータベース
ソフトウェアにはUTCオフセットだけでなく、各地域の完全な履歴と将来の規則が必要です。DSTの切り替え、政治的な変更、歴史的な異常を含みます。この情報はIANAタイムゾーンデータベース(Olsonデータベースまたはtzdataとも呼ばれます)に格納されています。
America/New_York、Europe/Paris、Asia/Tokyoのような識別子を使用します。各エントリは、その場所のUTCオフセットとDSTルールの完全な履歴をエンコードしています。
そのため、タイムゾーンを「+02:00」のような固定オフセットとして保存してはいけません。オフセットはUTCとの現在の差を示しますが、DSTのルールについては何も教えてくれません。Europe/Parisは冬はUTC+1、夏はUTC+2です。IANA識別子は両方をキャプチャします。
ソフトウェアでのよくあるバグ
- タイムゾーンなしでローカル時間を保存する。
2026-03-29 14:30:00のような値は、どのタイムゾーンを指しているか分からなければ意味がありません。常にUTCで保存するか、オフセットを含めてください。 - UTCオフセットをタイムゾーンと同一視する。 3月のUTC+2は7月にはUTC+3かもしれません(その地域がDSTを採用している場合)。オフセットではなく、IANA識別子を保存してください。
- スケジューリングでDSTの切り替えを無視する。 午前2時30分の日次ジョブは、DSTを処理しなければ年に1回スキップされ、年に1回2回実行されます。
- 1日が24時間だと仮定する。 DSTの切り替え日には、1日は23時間または25時間です。86,400秒を加算して「明日の同じ時刻」を計算すると、1時間ずれます。
- JavaScriptの
Dateをナイーブに使う。new Date("2026-03-29")は、一部のエンジンではUTCとして、他のエンジンではローカル時間としてパースされます。常にタイムゾーンを明示してください。
開発者のためのベストプラクティス
- 時刻はUTCで保存する。 ユーザーのローカルタイムゾーンへの変換は表示レイヤーでのみ行う。
- IANAタイムゾーン識別子を使用する(
America/New_York)。固定オフセット(-05:00)は使わない。 - シリアライゼーションにはISO 8601を使用する。 曖昧さがなく、普遍的にパース可能です。
- 成熟した日付ライブラリを使用する。 JavaScriptでは
Intl.DateTimeFormatやdate-fns-tz。Pythonではzoneinfo(3.9+)またはpytz。Javaではjava.time.ZonedDateTime。 tzdataを最新に保つ。 政府はDSTの規則を変更します。OSと言語ランタイムには最新のタイムゾーンデータが必要です。- 複数のタイムゾーンでテストする。 サーバーとユーザーが同じタイムゾーンを共有していると仮定しないでください。
さらに詳しく
時間は見た目以上に複雑ですが、ルールは十分に文書化されており、ツールも成熟しています。重要なのは、複雑さを仮定で排除するのではなく、尊重することです。
- Cron式を解き明かす — タイムゾーンをまたいだタスクスケジューリング
- ハッシュジェネレーターとRegexテスター — ToolKのその他の開発者ツール
