ライブラリなしで JavaScript のタイムゾーン正確な日付整形
JavaScript 組み込みの Intl.DateTimeFormat は、moment.js や date-fns なしで IANA タイムゾーンの整形を扱います。明示的な timeZone オプション、夏時間に安全な表示、formatToParts、壁時計変換の限界、Temporal やタイムゾーン ライブラリが今も役立つ場面を学びます。
Date になぜタイムゾーンのプロパティがないのか
JavaScript の Date は常に UTC ミリ秒のカウントです。オブジェクト内にタイムゾーンは保存されていません。オプションなしで .toString() や .toLocaleString() を呼ぶと、JavaScript は OS からのランタイムのローカルタイムゾーンを使います。同じコードでも、ニューヨークのサーバーと東京のノート PC では、基盤のタイムスタンプが同一でも異なる出力になります。
Intl.DateTimeFormat で整形する
Intl.DateTimeFormat は、ロケールとタイムゾーンを考慮した整形のための組み込み API です。IANA タイムゾーン識別子をサポートし、夏時間の切り替えを自動的に扱い、現代のブラウザと Node.js で利用できます。鍵は、ランタイムの既定に頼らず timeZone オプションを明示的に渡すことです。
- new Intl.DateTimeFormat('en-US', { timeZone: 'America/New_York', dateStyle: 'full', timeStyle: 'long' }).format(date)
- date.toLocaleString('en-GB', { timeZone: 'Europe/London', hour12: false })
- date.toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' })
- new Intl.DateTimeFormat('en-US', { timeZone: 'UTC', hour12: false }).format(date)
formatToParts で個々の要素を取り出す
formatToParts() を使って、日付の各要素を {type, value} オブジェクトとして取得し、独自の日付文字列を組み立てます。これは、ローカライズされた日付文字列を分割するより優れています。句読点・順序・文字体系がロケールによって異なるからです。
- const parts = new Intl.DateTimeFormat('en-US', { timeZone: 'America/Chicago', year: 'numeric', month: '2-digit', day: '2-digit' }).formatToParts(date)
- parts.find(p => p.type === 'year').value → '2023'
- parts.find(p => p.type === 'month').value → '11'
- Object.fromEntries(parts.map(p => [p.type, p.value])) → { year, month, day, hour, minute, second }
壁時計の時刻を UTC に変換する(難しい方向)
ある所定のタイムゾーンの壁時計の時刻から UTC へ向かうのは、従来の Date API では難しいです。ある瞬間を America/New_York で整形するのは簡単ですが、America/New_York における 2026-03-08 02:30 が表す瞬間を構築するのは簡単ではありません。そのローカル時刻は夏時間の切り替えで飛ばされたり繰り返されたりするからです。
- Temporal は 2026 年に Stage 4 に到達したが、ブラウザのネイティブ対応はまだ普遍的ではない
- 今日すべてのブラウザで本番利用するには、Temporal ポリフィルまたは date-fns-tz の toDate() が依然として実用的な選択肢
- 手動の UTC オフセット算術は避ける — 夏時間の切り替えは年によって異なるローカル時刻に起こる
- このサイトの zonedToEpochMs() は 1 回反復のオフセット補正を使う — src/timeUtils.ts を参照
正しいタイムゾーン識別子を選ぶ
America/New_York・Europe/London・Asia/Tokyo・UTC のような IANA タイムゾーン名を使ってください。ユーザー向けの時刻に UTC-5 のような固定オフセットを使うのは避けてください。オフセットは夏時間で変わるからです。America/New_York は 1 月に UTC-5、7 月に UTC-4 になりえます。IANA 名により、プラットフォームは選択された日付に対して正しい歴史的規則を適用できます。
- 良い:America/Los_Angeles — 過去と将来の夏時間規則を含む
- 良い:Europe/Berlin — 中央ヨーロッパ夏時間を自動的に扱う
- 良い:Asia/Shanghai — 夏時間なしの安定した UTC+8 表示
- ログ・API の保存・データベースのタイムスタンプ・リージョン間比較には UTC を使う
- プロダクト UI での最終表示にはユーザーの IANA タイムゾーンを使う
タイムゾーン整形 FAQ
- JavaScript はライブラリなしで別のタイムゾーンの日付を整形できる?
- はい。America/New_York や Asia/Tokyo のような timeZone オプションを付けて Intl.DateTimeFormat または toLocaleString を使います。
- Intl.DateTimeFormat は夏時間を扱う?
- はい。IANA タイムゾーン識別子を使うと、ランタイムはそのタイムゾーンと日付に対して正しいオフセットを適用します。
- タイムゾーンのオフセットと名前のどちらを保存すべき?
- イベントの瞬間には UTC を保存します。ユーザーのローカルな文脈が必要なら、数値オフセットだけでなく America/New_York のような IANA タイムゾーン名を保存します。
- JavaScript でユーザーのタイムゾーンを取得するには?
- Intl.DateTimeFormat().resolvedOptions().timeZone を使うと、America/New_York のような IANA 名が返ります。後でユーザーのローカル時刻を再構築する必要があるときは、数値オフセットではなくこの名前を保存します。