JavaScript Temporal APIの詳細ガイド。多様な国際的環境で日時を効果的に扱うための最新ソリューションを解説します。
JavaScript Temporal API: グローバルな利用者のためのモダンな日時処理
JavaScriptの`Date`オブジェクトは、長らく開発者の悩みの種でした。その可変性、一貫性のないAPI、そして貧弱なタイムゾーンサポートにより、Moment.jsやdate-fnsのような多数のライブラリがその欠点を補うために作られてきました。しかし今、Temporal APIの登場により、JavaScriptは日時をより明確かつ正確に扱うためのモダンな組み込みソリューションを提供します。この記事では、Temporal APIの包括的な概要を、その機能、利点、そして多様な国際的コンテキストでの使用方法に焦点を当てて解説します。
Temporal APIとは?
Temporal APIは、`Date`オブジェクトの欠点を解消するために設計された、JavaScriptの新しいグローバルオブジェクトです。日付、時刻、タイムゾーン、暦法を扱うための、クリーンで不変なAPIを提供します。重要なのは、日時に関する概念を現実世界の用法や期待により近い形で表現することを目指しており、国際化をはるかに容易にする点です。
主な特徴:
- 不変性(Immutability): Temporalオブジェクトは不変です。つまり、日や月を追加するような操作は、元のオブジェクトを変更するのではなく、新しいオブジェクトを返します。これにより、バグの一般的な原因が排除され、コードの理解が容易になります。
- 明確なAPI: Temporalは、一般的な日時操作のために一貫性のある直感的なAPIを提供します。
- タイムゾーンのサポート: Temporalは、タイムゾーンを強力にサポートしており、古い`Date`オブジェクトのような複雑さなしに、異なる場所の日時を扱うことができます。IANAタイムゾーンデータベースを使用しているため、正確で最新の情報が保証されます。
- 暦法システム: グレゴリオ暦だけでなく、Temporalは代替の暦法システムもサポートしており、多様な文化や地域のニーズに応えます。
- 精度の向上: Temporalはナノ秒単位の精度を提供し、ミリ秒ベースの`Date`オブジェクトの限界に対応します。
基本的なTemporalオブジェクト
Temporal APIはいくつかの新しいオブジェクト型を導入します。ここでは、その中核となるものをいくつか紹介します。
- `Temporal.PlainDate`: タイムゾーン情報を含まない日付(年、月、日)を表します。
- `Temporal.PlainTime`: 日付やタイムゾーン情報を含まない時刻(時、分、秒、ミリ秒、マイクロ秒、ナノ秒)を表します。
- `Temporal.PlainDateTime`: タイムゾーン情報を含まない日時を表します。
- `Temporal.ZonedDateTime`: 特定のタイムゾーンを持つ日時を表します。
- `Temporal.Instant`: Unixエポック(1970年1月1日 UTC)からのナノ秒で計測される、特定の瞬間を表します。
- `Temporal.TimeZone`: タイムゾーンを表します。
- `Temporal.Duration`: 時間の長さ(例:2時間30分)を表します。
- `Temporal.YearMonth`: 年と月を表します。
- `Temporal.MonthDay`: 月と日を表します。
日付の操作
`Temporal.PlainDate`の作成
`Temporal.PlainDate`を作成するには、コンストラクタを使用します。
const plainDate = new Temporal.PlainDate(2024, 10, 27); // 年、月(1-12)、日
console.log(plainDate.toString()); // 出力: 2024-10-27
また、ISO 8601形式の文字列を受け付ける`from`メソッドを使用することもできます。
const plainDateFromString = Temporal.PlainDate.from('2024-10-27');
console.log(plainDateFromString.toString()); // 出力: 2024-10-27
日付コンポーネントの取得
`year`、`month`、`day`などのプロパティを使用して、個々の日付コンポーネントにアクセスできます。
console.log(plainDate.year); // 出力: 2024
console.log(plainDate.month); // 出力: 10
console.log(plainDate.day); // 出力: 27
日付の計算
日、週、月、年を加算または減算するには、`plus`および`minus`メソッドを使用します。これらのメソッドは新しい`Temporal.PlainDate`オブジェクトを返します。
const nextWeek = plainDate.plus({ days: 7 });
console.log(nextWeek.toString()); // 出力: 2024-11-03
const lastMonth = plainDate.minus({ months: 1 });
console.log(lastMonth.toString()); // 出力: 2024-09-27
日付の比較
`compare`メソッドを使用して日付を比較できます。
const date1 = new Temporal.PlainDate(2024, 10, 27);
const date2 = new Temporal.PlainDate(2024, 11, 15);
console.log(Temporal.PlainDate.compare(date1, date2)); // 出力: -1 (date1はdate2より前)
時刻の操作
`Temporal.PlainTime`の作成
`Temporal.PlainTime`を作成するには、コンストラクタを使用します。
const plainTime = new Temporal.PlainTime(10, 30, 0); // 時、分、秒
console.log(plainTime.toString()); // 出力: 10:30:00
または、ISO 8601形式の時刻文字列で`from`メソッドを使用します。
const plainTimeFromString = Temporal.PlainTime.from('10:30:00');
console.log(plainTimeFromString.toString()); // 出力: 10:30:00
時刻コンポーネントの取得
console.log(plainTime.hour); // 出力: 10
console.log(plainTime.minute); // 出力: 30
console.log(plainTime.second); // 出力: 0
時刻の計算
const later = plainTime.plus({ minutes: 15 });
console.log(later.toString()); // 出力: 10:45:00
日付と時刻の同時操作
`Temporal.PlainDateTime`の作成
`Temporal.PlainDateTime`は直接作成することも、`Temporal.PlainDate`と`Temporal.PlainTime`を組み合わせて作成することもできます。
const plainDateTime = new Temporal.PlainDateTime(2024, 10, 27, 10, 30, 0);
console.log(plainDateTime.toString()); // 出力: 2024-10-27T10:30:00
const date = new Temporal.PlainDate(2024, 10, 27);
const time = new Temporal.PlainTime(10, 30, 0);
const combinedDateTime = date.toPlainDateTime(time);
console.log(combinedDateTime.toString()); // 出力: 2024-10-27T10:30:00
タイムゾーン
異なる場所にいるユーザーを扱うアプリケーションにとって、タイムゾーンを正しく処理することは非常に重要です。Temporal APIは、`Temporal.ZonedDateTime`および`Temporal.TimeZone`オブジェクトを通じて、強力なタイムゾーンサポートを提供します。
`Temporal.ZonedDateTime`の作成
`Temporal.ZonedDateTime`を作成するには、`Temporal.PlainDateTime`とタイムゾーン識別子が必要です。タイムゾーン識別子はIANAタイムゾーンデータベース(例: `America/Los_Angeles`, `Europe/London`, `Asia/Tokyo`)に基づいています。
const plainDateTime = new Temporal.PlainDateTime(2024, 10, 27, 10, 30, 0);
const timeZone = 'America/Los_Angeles';
const zonedDateTime = plainDateTime.toZonedDateTime(timeZone);
console.log(zonedDateTime.toString()); // 出力: 2024-10-27T10:30:00-07:00[America/Los_Angeles] (オフセットは夏時間ルールに依存します)
あるいは、`Instant`から`Temporal.ZonedDateTime`を作成することもできます。
const instant = Temporal.Instant.fromEpochSeconds(1666866600); // タイムスタンプの例
const zonedDateTimeFromInstant = instant.toZonedDateTimeISO(timeZone); // 'America/Los_Angeles'のようなタイムゾーン
console.log(zonedDateTimeFromInstant.toString());
タイムゾーン間の変換
`withTimeZone`メソッドを使用して、`Temporal.ZonedDateTime`を異なるタイムゾーンに変換できます。
const newTimeZone = 'Europe/London';
const zonedDateTimeInLondon = zonedDateTime.withTimeZone(newTimeZone);
console.log(zonedDateTimeInLondon.toString()); // 出力: 2024-10-27T18:30:00+01:00[Europe/London]
タイムゾーンオフセットの操作
`Temporal.TimeZone`オブジェクトの`getOffsetStringFor`メソッドは、指定された`Temporal.Instant`に対するオフセット文字列を提供します。
const timeZoneObject = new Temporal.TimeZone(timeZone);
const offsetString = timeZoneObject.getOffsetStringFor(zonedDateTime.toInstant());
console.log(offsetString); // 出力: -07:00 (夏時間ルールに依存)
正確な計算のためには、正しいIANAタイムゾーン識別子を使用することが不可欠です。これらの識別子は、夏時間やタイムゾーン境界の変更を反映するために、定期的に維持および更新されます。
期間(Duration)
`Temporal.Duration`オブジェクトは時間の長さを表します。日時に対して加算または減算するために使用できます。
`Temporal.Duration`の作成
コンストラクタを使用して`Temporal.Duration`を作成できます。年、月、日、時、分、秒、ミリ秒、マイクロ秒、ナノ秒を指定します。
const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9); // 年, 月, 日, 時, 分, 秒, ミリ秒, マイクロ秒, ナノ秒
console.log(duration.toString()); // 出力: P1Y2M3DT4H5M6.007008009S
または、ISO 8601期間文字列を使用します。
const durationFromString = Temporal.Duration.from('P1Y2M3DT4H5M6S');
console.log(durationFromString.toString()); // 出力: P1Y2M3DT4H5M6S
日時への期間の追加
const plainDate = new Temporal.PlainDate(2024, 10, 27);
const duration = new Temporal.Duration(0, 0, 7); // 7日間
const newDate = plainDate.plus(duration);
console.log(newDate.toString()); // 出力: 2024-11-03
月や年を含む期間を日付に加算する場合、月や年の日数が変動するため、注意深い検討が必要であることに注意してください。
暦法システム
Temporal APIは、グレゴリオ暦以外のさまざまな暦法システムをサポートしています。これは、多様な文化的背景で日付を扱う必要があるアプリケーションにとって非常に重要です。サポートはまだ発展途上ですが、将来の拡張のための基盤を提供します。
代替暦の使用
特定の暦を使用するには、Temporalオブジェクトを作成する際にそれを指定します。
const hebrewDate = new Temporal.PlainDate(5785, 1, 1, { calendar: 'hebrew' });
console.log(hebrewDate.toString()); // 具体的な出力は実装やフォーマットによって異なる場合があります。この記事の執筆時点では、多くの環境でポリフィルが必要です。
重要: グレゴリオ暦以外の暦のサポートには、ポリフィルや特定のブラウザ/環境のサポートが必要になる場合があります。最新情報については、Temporal APIのドキュメントやブラウザ互換性テーブルを確認してください。
日時のフォーマット
Temporal APIは日時の操作に焦点を当てていますが、フォーマットは通常、国際化APIの一部である`Intl.DateTimeFormat`オブジェクトによって処理されます。Temporalオブジェクトは`Intl.DateTimeFormat`とシームレスに連携します。
`Intl.DateTimeFormat`の使用
以下に`Intl.DateTimeFormat`を使用して`Temporal.PlainDate`をフォーマットする方法を示します。
const plainDate = new Temporal.PlainDate(2024, 10, 27);
const formatter = new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
console.log(formatter.format(plainDate)); // 出力: October 27, 2024
const formatterGerman = new Intl.DateTimeFormat('de-DE', { year: 'numeric', month: 'long', day: 'numeric' });
console.log(formatterGerman.format(plainDate)); // 出力: 27. Oktober 2024
フォーマットオプションは、ニーズに合わせてカスタマイズできます。`Intl.DateTimeFormat`の最初の引数はロケールであり、フォーマットに使用される言語と地域の慣習を決定します。異なるロケール(例: 'en-US', 'de-DE', 'fr-FR', 'ja-JP')を使用すると、異なる出力形式が生成されます。
`Temporal.ZonedDateTime`のフォーマット
`Temporal.ZonedDateTime`のフォーマットも同様ですが、出力にタイムゾーン情報を含めることもできます。
const plainDateTime = new Temporal.PlainDateTime(2024, 10, 27, 10, 30, 0);
const timeZone = 'America/Los_Angeles';
const zonedDateTime = plainDateTime.toZonedDateTime(timeZone);
const formatter = new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric', timeZoneName: 'short' });
console.log(formatter.format(zonedDateTime)); // 出力: October 27, 2024, 10:30 AM PDT (タイムゾーンの略称は夏時間ルールに依存します)
国際化のベストプラクティス
グローバルなコンテキストで日時を扱う際には、以下のベストプラクティスを念頭に置いてください。
- IANAタイムゾーン識別子を使用する: 正確なタイムゾーン処理のために、常にIANAタイムゾーン識別子(例: `America/Los_Angeles`, `Europe/London`)を使用してください。
- 夏時間に注意する: 夏時間(DST)はタイムゾーンオフセットに影響を与える可能性があります。Temporal APIはDSTの移行を自動的に処理します。
- フォーマットには`Intl.DateTimeFormat`を使用する: ユーザーのロケールに従って日時をフォーマットするには、`Intl.DateTimeFormat`オブジェクトを使用してください。
- 暦法システムを考慮する: アプリケーションが異なる文化的背景を持つユーザーをサポートする必要がある場合は、代替の暦法システムの使用を検討してください。
- 日時はUTCで保存する: データベースに日時を保存する際は、タイムゾーンの問題を避けるためにUTC(協定世界時)で保存するのがベストプラクティスです。その後、表示目的でローカル時刻に変換します。TemporalはUTCとの間で変換するメソッドを提供します。
- 徹底的にテストする: アプリケーションがすべてのユーザーに対して正しく機能することを確認するために、異なるタイムゾーン、ロケール、暦法システムでテストしてください。
Temporal APIと従来のDateオブジェクトの比較
以下は、Temporal APIと従来の`Date`オブジェクトの主な違いと利点を強調した表です。
機能 | 従来の`Date`オブジェクト | Temporal API |
---|---|---|
可変性 | 可変(元のオブジェクトを変更する) | 不変(新しいオブジェクトを返す) |
タイムゾーンサポート | 限定的で問題が多い | IANAタイムゾーンデータベースに基づき、堅牢で正確 |
API | 一貫性がなく、使いにくい | 明確で、一貫性があり、直感的 |
精度 | ミリ秒 | ナノ秒 |
暦法システム | グレゴリオ暦に限定 | 代替の暦法システムをサポート(サポートは発展途上) |
国際化 | 堅牢な国際化には外部ライブラリが必要 | 組み込みサポートと`Intl.DateTimeFormat`とのシームレスな統合 |
ブラウザサポートとポリフィル
比較的新しいAPIであるため、Temporal APIのブラウザサポートはまだ発展途上です。最新のブラウザ互換性テーブル(例:MDN Web Docs)をチェックして、どのブラウザや環境がネイティブでサポートしているかを確認してください。ネイティブサポートがない古いブラウザや環境向けには、ポリフィルを使用してTemporal APIの機能を提供できます。適切なオプションを見つけるには、ウェブで「Temporal API polyfill」を検索してください。
結論
JavaScriptのTemporal APIは、JavaScriptにおける日時処理の大きな前進を意味します。その不変性、明確なAPI、堅牢なタイムゾーンサポート、そして暦法システムの機能は、多様な国際的コンテキストで日時を正確かつ確実に扱う必要があるアプリケーションを構築する開発者にとって、強力なツールとなります。ブラウザのサポートはまだ発展途上ですが、Temporal APIの利点は、新しいプロジェクトで学習し採用する価値があるものです。Temporal APIを受け入れ、国際化のベストプラクティスに従うことで、世界中のユーザーにシームレスで正確な日時体験を提供するアプリケーションを作成できます。