【JavaScript】Temporalによる日時操作を学ぶ
はじめに
この記事の概要
こんにちは、株式会社TOKOSのスギタです!
今回はJavaScriptで標準化された(2026年3月現在)Temporal APIについて解説します。
Temporal APIはDateオブジェクトの問題点を解決するためのAPIです。
対象読者
- フロントエンドエンジニアの方
この記事で扱う内容、扱わない内容
この記事で扱う内容は下記です。
Dateオブジェクトの問題点TemporalAPIについて
この記事で扱わない内容は下記です。
- JavaScriptの基本的な使い方
JavaScriptの標準化について
今回のTemporal APIを学ぶ前にJavaScriptの標準化されるまでの流れを簡単に紹介します。
JavaScriptは現在ECMAScriptという規格で標準化されています。
標準化を行う際にTC39という委員会が標準化を行っています。
TC39 - Specifying JavaScript.
tc39.es標準化をする際はProposalという形で提案されます。
ProposalはTC39のメンバーが提案し、Stage 0からStage 4まで(Stage 2.7を含む6段階)で進んでいきます。
- Stage 0 : Strawperson(アイデア段階)
- 目的 : 問題領域の探索とアイデア出し
- 意味 : 新しいアイデアの初期探索段階
- Stage 1 : Proposal(提案)
- 目的 : 特定のアプローチで解決策を設計する
- 意味 : 委員会がその問題領域を検討することにコミット
- Stage 2 : Draft(草案)
- 目的 : 詳細の詰め、スペックレビュー、実験的実装
- 意味 : 委員会が優先する解決方向を選択(大きな設計変更はまだあり得る)
- Stage 2.7 : Candidate(候補 - テスト段階)
- 目的 : テストコードの作成とスペック準拠のプロトタイプによる検証
- 意味 : 解決策は完成しており、変更はテストや実装からのフィードバックに限定
- Stage 3 : Candidate(候補 - 実装段階)
- 目的 : 実装経験の蓄積と互換性の問題発見
- 意味 : 実装が推奨される。基本的に変更は想定されない
- Stage 4 : Finished(完了)
- 目的 : 年次出版の標準への統合
- 意味 : ECMAScript標準に組み込まれる
月に一度会議が開かれ、Proposalが議論されています。
GitHub - tc39/agendas: TC39 meeting agendas
TC39 meeting agendas. Contribute to tc39/agendas development by creating an account on GitHub.
github.comまた、今現在も魅力的な様々なProposalが存在しています。
GitHub - tc39/proposals: Tracking ECMAScript Proposals
Tracking ECMAScript Proposals. Contribute to tc39/proposals development by creating an account on GitHub.
github.comちなみにTemporalは2017年に提案されました。
なぜ今Temporalを学ぶのか
では、なぜ今Temporalを紹介するのか、その理由は下記になります。
- 最近Stage 4になったため
- 主要ブラウザで対応されたため
2026年3月11日に開催された、第113回のTC39の総会でStage 4になりました。
また、Chrome 144以降で利用可能になります。
対応ブラウザ及び、バージョンは下記です。
- Google Chrome 144
- Firefox 139
- Edge 144
Temporal - JavaScript | MDN
Temporal オブジェクトは、組み込みのタイムゾーンおよび暦の表現、実時刻の変換、算術演算、書式化など、さまざまなシナリオで日付と時刻の管理を可能にします。これは、 Date オブジェクトを完全に置き換えるものとして設計されています。
developer.mozilla.orgまだ、Safari等使用できないブラウザもありますが、今後対応されることが期待されています。
というわけで、今後はDateオブジェクトからTemporalが主流になっていく事が考えられます。
Dateオブジェクトの問題点とTemporalの改善
現在主流であるDateオブジェクトの日時操作の問題点とTemporalによって改善された点を簡単に紹介します。
Dateオブジェクトに関しては下記の記事で詳しく紹介しています。
【JavaScript】Dateオブジェクトとタイムゾーンと規格について
この記事ではJavaScriptでDateを扱う際の注意点と扱い方の基礎が学べます。今現在時刻の呼び出しや計算方法も付随して学べます。
ミュータブル(可変)である
Dateオブジェクトはミュータブル(可変)であるため、不整合が発生しやすいです。
setMonthメソッドなどを呼ぶと、元のDateオブジェクトが変更されてしまいます。
const meeting = new Date(2026, 2, 16)
const reminder = meeting // 参照コピー!
reminder.setDate(meeting.getDate() - 1)
console.log(meeting.toDateString())
// → "Sun Mar 15 2026"
// meeting も変わってしまう!Temporalはイミュータブル(不変)であるため、元のオブジェクトが変更されずに新しいオブジェクトが作成されます。
const meeting = Temporal.PlainDate.from("2026-03-16")
const reminder = meeting.subtract({ days: 1 })
console.log(meeting.toString())
// → "2026-03-16" ✅ 元のまま
console.log(reminder.toString())
// → "2026-03-15" ✅ 新しいオブジェクトタイムゾーンのサポートが不十分
DateはUTCとローカル時間しか扱えず、任意のタイムゾーンを明示的に指定して操作する仕組みがありません。
異なるタイムゾーン間の変換が非常に面倒です。
// UTC と ローカルしかない
const date = new Date()
console.log(date.toISOString())
// → UTC のみ
console.log(
date.toLocaleString("ja-JP", {
timeZone: "America/New_York",
}),
)
// → 文字列としては表示できるが
// Date オブジェクトとして
// NYの時刻を持つことはできない 😱Temporalではタイムゾーンを持つ日時を表すZonedDateTimeを使用することで任意のタイムゾーンを扱うことができます。
// 任意のタイムゾーンをネイティブにサポート
const tokyo = Temporal.Now.zonedDateTimeISO("Asia/Tokyo")
const ny = tokyo.withTimeZone("America/New_York")
console.log(tokyo.toString())
// → "2026-03-16T15:30:00+09:00[Asia/Tokyo]"
console.log(ny.toString())
// → "2026-03-16T01:30:00-05:00[America/New_York]"
// ✅ 型としてタイムゾーン情報を保持月が0から始まる
new Date(2026, 0, 1)が1月1日を意味するという直感に反する仕様で、off-by-oneエラーが頻発します。
// 3月を指定したつもりが...
const date = new Date(2026, 3, 1)
console.log(date.toDateString())
// → "Wed Apr 01 2026" 😱
// 3 は4月!3月は 2 と書く必要がある
const march = new Date(2026, 2, 1)
console.log(march.getMonth())
// → 2 🤔 3月なのに2Temporalでは直感的に記述できます。
// 素直に3月と書ける
const date = Temporal.PlainDate.from({
year: 2026,
month: 3,
day: 1,
})
console.log(date.toString())
// → "2026-03-01" ✅
console.log(date.month)
// → 3 ✅ 3月は3月のオーバーフロー
存在しない月日を指定すると、繰り越されてしまいます。
エラーや警告が出ないため、バグに気づきにくいです。
const jan31 = new Date(2026, 0, 31)
// 2月にしたい
jan31.setMonth(1)
console.log(jan31.toDateString())
// → "Tue Mar 03 2026" 😱
// 2/31は存在しない → 3/3に繰り越し
// エラーも警告もなし!Temporalではこのような問題が発生しません。
const jan31 = Temporal.PlainDate.from("2026-01-31")
// overflow: "constrain" で安全に丸める
const feb = jan31.with({ month: 2 }, { overflow: "constrain" })
console.log(feb.toString())
// → "2026-02-28" ✅
// overflow: "reject" でエラーにもできる
Temporal.PlainDate.from({ year: 2026, month: 2, day: 31 }, { overflow: "reject" })
// → RangeError ✅ 明示的にエラー日付/時刻のみの参照ができない
Dateは常に日時の両方を持つため、「誕生日」のように日付だけが必要なケースや、「営業時間」のように時刻だけが必要なケースに適しません。
// 誕生日を表現したいだけなのに...
const birthday = new Date(1990, 4, 15)
console.log(birthday.toISOString())
// → "1990-05-14T15:00:00.000Z"
// 不要な時刻情報がついてくる
// しかもタイムゾーンで日付がズレる!
// 営業時間 "9:00-17:00" も表現できない
// 必ず日付が必要Temporalでは任意の年月日や時刻等が取得できます。
// 日付だけ
const birthday = Temporal.PlainDate.from("1990-05-15")
console.log(birthday.toString())
// → "1990-05-15" ✅ 日付のみ
// 時刻だけ
const openTime = Temporal.PlainTime.from("09:00")
const closeTime = Temporal.PlainTime.from("17:00")
console.log(openTime.toString())
// → "09:00:00" ✅ 時刻のみ
// 年月だけ
const billing = Temporal.PlainYearMonth.from("2026-03")
// → "2026-03" ✅文字列パースが不安定
Date.parse()やnew Date("...")の挙動がブラウザによって異なり、同じ文字列が異なる結果になることがあります。
// ブラウザによって結果が違う!
const d1 = new Date("2026-03-16")
// → UTC? ローカル? ブラウザ次第
const d2 = new Date("03/16/2026")
// → パース可能だが非標準
const d3 = new Date("March 16, 2026")
// → 動くかもしれないし動かないかも
const d4 = new Date("abc")
// → Invalid Date(例外ではない!)
console.log(d4.getTime()) // → NaNTemporalではISO 8601/RFC 9557に準拠しています。
// 厳密なISO 8601 / RFC 9557 準拠
const d1 = Temporal.PlainDate.from("2026-03-16")
// → 常に同じ結果 ✅
// 不正な文字列は即座にエラー
try {
Temporal.PlainDate.from("abc")
} catch (e) {
console.log(e) // → RangeError ✅
}
// オブジェクト形式も使える
const d2 = Temporal.PlainDate.from({
year: 2026,
month: 3,
day: 16,
})
// → 明確で安全 ✅上記で挙げたもの以外にもDateオブジェクトの問題点はあり、多くのプロダクトでは日時操作系のライブラリを使用していると思います。
Temporalの全体像
Temporalは大別すると2つに分けることができます。
- タイムゾーンなし
PlainDateTime
- タイムゾーンあり
ZonedDateTime
下記が図解になります。
PlainDateTimeについて
PlainDateTimeの概要
PlainDateTimeはタイムゾーンを持たない日時を表します。
PlainDateTimeが適しているのは、タイムゾーンが暗黙的に決まっている、あるいは無関係な場面です。
たとえば「3月16日14:30に会議室Aで打ち合わせ」という予定は、参加者全員が同じオフィスにいるなら、タイムゾーンを意識する必要はありません。
壁掛け時計が14:30を指していれば、それが正しい時刻です。
こうした「ローカルな日時」を扱うのがPlainDateTimeの役割です。
// ISO 8601 文字列から
const dt = Temporal.PlainDateTime.from("2026-03-16T14:30:00")
console.log(dt.toString())
// → "2026-03-16T14:30:00"
// オブジェクトから
const dt2 = Temporal.PlainDateTime.from({
year: 2026,
month: 3,
day: 16,
hour: 14,
minute: 30,
second: 0,
})
console.log(dt2.toString())
// → "2026-03-16T14:30:00"
// PlainDate + PlainTime を合成
const date = Temporal.PlainDate.from("2026-03-16")
const time = Temporal.PlainTime.from("14:30")
const dt3 = date.toPlainDateTime(time)
// → "2026-03-16T14:30:00"PlainDateTimeの読み取り専用メソッド
PlainDateTimeは日付側と時刻側の両方のメソッドを持ちます。
すべて読み取り専用メソッドです。
const dt = Temporal.PlainDateTime.from("2026-03-16T14:30:45.123")
// 日付フィールド
dt.year // → 2026
dt.month // → 3(1始まり!)
dt.day // → 16
dt.dayOfWeek // → 1(月曜日。ISO 8601準拠で1=月〜7=日)
dt.dayOfYear // → 75
dt.weekOfYear // → 12
dt.daysInMonth // → 31
dt.daysInYear // → 365
dt.inLeapYear // → false
// 時刻フィールド
dt.hour // → 14
dt.minute // → 30
dt.second // → 45
dt.millisecond // → 123
dt.microsecond // → 0
dt.nanosecond // → 0PlainDateTimeの変更操作 withメソッド
withメソッドを使用することで変更できます。
Temporalはイミュータブルであるため、新しいオブジェクトを返します。
const dt = Temporal.PlainDateTime.from("2026-03-16T14:30")
// 時刻だけ変更
dt.with({ hour: 9, minute: 0 })
// → "2026-03-16T09:00:00"
// 日付だけ変更
dt.with({ month: 12, day: 25 })
// → "2026-12-25T14:30:00"
// 元のオブジェクトは変わらない
dt.toString() // → "2026-03-16T14:30:00"withメソッドはオーバーフロー制御もあります。
// 4月31日は存在しない
Temporal.PlainDateTime.from("2026-03-31T10:00").with(
{ month: 4 },
{ overflow: "constrain" }, // デフォルト
)
// → "2026-04-30T10:00:00"(最も近い有効な日に丸められる)
Temporal.PlainDateTime.from("2026-03-31T10:00").with({ month: 4 }, { overflow: "reject" })
// → RangeError(エラーを投げる)PlainDateTimeの加減算 add/subtractメソッド
add(加算)/subtract(減算)メソッドを使用することで加減算を行うことができます。
const dt = Temporal.PlainDateTime.from("2026-03-16T14:30")
dt.add({ hours: 3, minutes: 45 })
// → "2026-03-16T18:15:00"
dt.add({ days: 1, hours: 12 })
// → "2026-03-18T02:30:00"(日をまたぐ)
dt.subtract({ months: 2 })
// → "2026-01-16T14:30:00"ここで重要なのは、PlainDateTimeにはタイムゾーンがないため、DST(夏時間)の影響を受けないことです。
「14:30の2時間後は常に16:30」になります。
実際のタイムゾーンではDSTの切り替えで1時間消えたり増えたりしますが、PlainDateTimeならそうした複雑さを考慮しなくて良いです。
PlainDateTimeの差分の計算 until/sinceメソッド
until(終了時刻)/since(開始時刻)メソッドを使用することで差分を計算できます。
const start = Temporal.PlainDateTime.from("2026-03-16T09:00")
const end = Temporal.PlainDateTime.from("2026-03-16T17:30")
start.until(end)
// → PT8H30M(8時間30分)
start.until(end, { largestUnit: "minute" })
// → PT510M(510分)
// 日をまたぐ場合
const overnight = Temporal.PlainDateTime.from("2026-03-17T02:00")
start.until(overnight, { largestUnit: "hour" })
// → PT17H(17時間)PlainDateTimeの繰り上げ/切り捨ての計算 roundメソッド
roundメソッドを使用することで繰り上げ/繰り下げを計算できます。
const dt = Temporal.PlainDateTime.from("2026-03-16T14:37:22")
dt.round("hour")
// → "2026-03-16T15:00:00"(繰り上げ)
dt.round({ smallestUnit: "minute", roundingIncrement: 15 })
// → "2026-03-16T14:30:00"(15分刻みで丸め)
dt.round({
smallestUnit: "hour",
roundingMode: "floor",
})
// → "2026-03-16T14:00:00"(切り捨て)PlainDateTimeの他の型への変換
PlainDateTimeは他のTemporal型への変換に使用できます。
const dt = Temporal.PlainDateTime.from("2026-03-16T14:30")
// 日付だけ取り出す
dt.toPlainDate() // → Temporal.PlainDate "2026-03-16"
// 時刻だけ取り出す
dt.toPlainTime() // → Temporal.PlainTime "14:30:00"
// タイムゾーンを付与して ZonedDateTime に変換
dt.toZonedDateTime("Asia/Tokyo")
// → "2026-03-16T14:30:00+09:00[Asia/Tokyo]"
dt.toZonedDateTime("America/New_York")
// → "2026-03-16T14:30:00-04:00[America/New_York]"
// ※同じ壁時計の時刻だが、指す「瞬間」は異なるPlainDateTimeの比較
比較系のメソッドを使用することで比較できます。
const a = Temporal.PlainDateTime.from("2026-03-16T14:30")
const b = Temporal.PlainDateTime.from("2026-03-16T18:00")
Temporal.PlainDateTime.compare(a, b) // → -1(a < b)
a.equals(b) // → false
// ソートにも使える
const events = [b, a]
events.sort(Temporal.PlainDateTime.compare)
// → [a, b](時系列順)PlainDateTimeのまとめ
PlainDateTimeはタイムゾーン情報を持たず「壁掛け時計が示す日時」を表現します。
「タイムゾーンを意識しなくてよい場面で使う軽量な型」であり、タイムゾーンが絡む瞬間にtoZonedDateTimeで明示的に文脈を与えるという設計になっています。
ですので、東京とニューヨークのメンバーが参加するオンライン会議のように、「全員が同じ瞬間に集まる」必要がある場合はZonedDateTimeを使います。
ZonedDateTimeについて
ZonedDateTimeの概要
ZonedDateTimeはタイムゾーンを持つ日時を表します。
日付、時刻、タイムゾーン、カレンダーのすべてを持ち、地球上の特定の「瞬間」を一意に表現します。
「全員が同じ瞬間を指している」ことが重要な場面で使います。
// 文字列から(RFC 9557形式)
const zdt = Temporal.ZonedDateTime.from("2026-03-16T14:30:00+09:00[Asia/Tokyo]")
console.log(zdt.toString())
// → "2026-03-16T14:30:00+09:00[Asia/Tokyo]"
// オブジェクトから
const zdt2 = Temporal.ZonedDateTime.from({
year: 2026,
month: 3,
day: 16,
hour: 14,
minute: 30,
timeZone: "Asia/Tokyo",
})
console.log(zdt2.toString())
// → "2026-03-16T14:30:00+09:00[Asia/Tokyo]"
// 現在時刻から(最もよく使うパターン)
const now = Temporal.Now.zonedDateTimeISO("Asia/Tokyo")ZonedDateTimeの読み取り専用メソッド
PlainDateTimeのプロパティに加えて、タイムゾーン関連の情報を持ちます。
const zdt = Temporal.ZonedDateTime.from("2026-03-16T14:30:00+09:00[Asia/Tokyo]")
// 日付・時刻(PlainDateTimeと同じ)
zdt.year // → 2026
zdt.month // → 3
zdt.day // → 16
zdt.hour // → 14
zdt.minute // → 30
// タイムゾーン固有
zdt.timeZoneId // → "Asia/Tokyo"
zdt.offset // → "+09:00"
zdt.offsetNanoseconds // → 32400000000000
zdt.epochNanoseconds // → BigInt(Unix epoch からのナノ秒)
zdt.epochMilliseconds // → number(Date互換)
// その日のDST対応した時間を取得
zdt.hoursInDay // → 24(DSTの日は23や25になる)hoursInDayはDSTの切り替え日には23や25を返します。
「今日は何時間あるか」を正確に知ることができます。
タイムゾーンの変換 withTimeZoneメソッド
タイムゾーンを変更するメソッドです。
同じ「瞬間」を別のタイムゾーンの壁時計で見たらどうなるか、を一行で表現できます。
const tokyo = Temporal.ZonedDateTime.from("2026-03-16T14:30:00+09:00[Asia/Tokyo]")
const ny = tokyo.withTimeZone("America/New_York")
// → "2026-03-16T01:30:00-04:00[America/New_York]"
const london = tokyo.withTimeZone("Europe/London")
// → "2026-03-16T05:30:00+00:00[Europe/London]"
// 同じ瞬間であることを確認
tokyo.epochNanoseconds === ny.epochNanoseconds // → true
tokyo.epochNanoseconds === london.epochNanoseconds // → trueZonedDateTimeの変更操作 withメソッド
withメソッドを使用することで変更できます。
const zdt = Temporal.ZonedDateTime.from("2026-03-16T14:30:00+09:00[Asia/Tokyo]")
// 時刻を変更(タイムゾーンは維持)
zdt.with({ hour: 9, minute: 0 })
// → "2026-03-16T09:00:00+09:00[Asia/Tokyo]"
// 日付を変更
zdt.with({ month: 12, day: 25 })
// → "2026-12-25T14:30:00+09:00[Asia/Tokyo]"ZonedDateTimeの加減算 add/subtractメソッド
PlainDateTimeと異なり、ZonedDateTimeの加減算はDSTの影響を正しく反映します。
// アメリカ東部:2026年3月8日に夏時間開始(時計が1時間進む)
const beforeDST = Temporal.ZonedDateTime.from("2026-03-07T12:00:00-05:00[America/New_York]")
beforeDST.add({ days: 1 })
// → "2026-03-08T12:00:00-04:00[America/New_York]"
// 壁時計上は同じ12:00だが、オフセットが -05:00 → -04:00 に変化
// 実際に経過した時間は23時間
beforeDST.add({ hours: 24 })
// → "2026-03-08T13:00:00-04:00[America/New_York]"
// 24時間後は壁時計上では13:00(DSTで1時間進んだため)ZonedDateTimeの差分の計算 until/sinceメソッド
until(終了時刻)/since(開始時刻)メソッドを使用することで差分を計算できます。
タイムゾーンによる時差を計算できます。
const departure = Temporal.ZonedDateTime.from("2026-07-01T10:00:00+09:00[Asia/Tokyo]")
const arrival = Temporal.ZonedDateTime.from("2026-07-01T14:00:00-04:00[America/New_York]")
departure.until(arrival, { largestUnit: "hour" })
// → PT17H(実際のフライト時間は17時間)
// タイムゾーンの差を自動的に考慮その日の始まり startOfDayメソッド
DSTの切り替え日では00:00が存在しない場合もあります。
startOfDayメソッドを使用することで、その日の始まりを正しく取得できます。
const zdt = Temporal.ZonedDateTime.from("2026-03-16T14:30:00+09:00[Asia/Tokyo]")
zdt.startOfDay()
// → "2026-03-16T00:00:00+09:00[Asia/Tokyo]"
// DSTの切り替え日は 00:00 が存在しないことがある
// startOfDay はその場合でも正しい「その日の最初の瞬間」を返すZonedDateTimeの他の型への変換
ZonedDateTimeは他の型への変換に使用できます。
const zdt = Temporal.ZonedDateTime.from("2026-03-16T14:30:00+09:00[Asia/Tokyo]")
// タイムゾーンを剥がす
zdt.toPlainDateTime() // → "2026-03-16T14:30:00"
zdt.toPlainDate() // → "2026-03-16"
zdt.toPlainTime() // → "14:30:00"
// 絶対時刻に変換
zdt.toInstant()
// → Instant(タイムゾーンなし、エポックナノ秒のみ)
// Dateオブジェクトとの相互変換
const legacyDate = new Date(zdt.epochMilliseconds)
// Date → ZonedDateTime
const fromLegacy = Temporal.Instant.fromEpochMilliseconds(legacyDate.getTime()).toZonedDateTimeISO("Asia/Tokyo")ZonedDateTimeの比較
PlainDateTimeとは違い、ZonedDateTimeの場合はタイムゾーンを持つため結果が異なります。
const a = Temporal.ZonedDateTime.from("2026-03-16T14:30:00+09:00[Asia/Tokyo]")
const b = Temporal.ZonedDateTime.from("2026-03-16T01:30:00-04:00[America/New_York]")
// compare は「瞬間」で比較する
Temporal.ZonedDateTime.compare(a, b) // → 0(同じ瞬間)
// equals は瞬間 + タイムゾーン + カレンダーすべてが一致する必要がある
a.equals(b) // → false(タイムゾーンが異なる)ZonedDateTimeのまとめ
ZonedDateTimeはTemporalの型体系の中心に位置する型です。
迷ったらまずZonedDateTimeから始め、タイムゾーンが不要だとわかった時点でtoPlainDateTime()などで軽量な型に変換する、というアプローチが安全になります。
Instantについて
Temporal.Instantはタイムゾーンやカレンダーを持たない、純粋な「時間軸上の一点」です。
Unixエポック(1970-01-01T00:00:00Z)からのナノ秒で表現されます。
データベースの作成・更新時刻の記録などに向いています。
また、toString()したときにUTC表記(末尾Z)で出力されるので「UTCが取れる」ように見えますが、実態はこういう構造です。
Instant の中身:
epochNanoseconds = 1773639000000000000n ← これだけ
toString() すると:
"2026-03-16T05:30:00Z" ← UTC表記で「見せている」Temporal.Nowについて
Temporal.Nowは現在時刻を取得するメソッドです。
Dateオブジェクトのnew Date()/Date.now()に該当します。
5つのメソッドが存在します。
zonedDateTimeISO("Asia/Tokyo") // → 2026-03-16T04:16:43+09:00[Asia/Tokyo]
instant() // → 2026-03-15T19:16:43Z
plainDateTimeISO("Asia/Tokyo") // → 2026-03-16T04:16:43
plainDateISO("Asia/Tokyo") // → 2026-03-16
plainTimeISO("Asia/Tokyo") // → 04:16:43instant()以外はすべてタイムゾーン引数を受け取ります。
省略するとシステムのタイムゾーンが使われます。
Dateオブジェクトの対応は下記になります。
new Date() → Temporal.Now.zonedDateTimeISO()
Date.now() → Temporal.Now.instant().epochMillisecondsDurationについて
Durationの概要
Temporal.Durationは時間の長さ(期間)を表す型です。
// ISO 8601 文字列から
const d1 = Temporal.Duration.from("P1Y2M3DT4H5M6S")
// → 1年2ヶ月3日4時間5分6秒
// オブジェクトから
const d2 = Temporal.Duration.from({
years: 1,
months: 2,
days: 3,
hours: 4,
minutes: 5,
seconds: 6,
})
// 個別のフィールドだけでもOK
const d3 = Temporal.Duration.from({ hours: 2, minutes: 30 })
// → "PT2H30M0S"ISO 8601のP...T...形式は、Pが期間の開始を示し、Tが日付部分と時刻部分の区切りです。
P1Y2M3Dは「1年2ヶ月3日」、T4H5M6Sは「4時間5分6秒」を意味します。
Durationの読み取り専用メソッド
Durationは下記の10個の読み取り専用メソッドを持ちます。
years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds
const d = Temporal.Duration.from({ hours: 2, minutes: 30 })
d.hours // → 2
d.minutes // → 30
d.days // → 0(未指定のフィールドは0)他の型との連携
Durationの真価は他のTemporal型と組み合わせたときに発揮されます。
const today = Temporal.PlainDate.from("2026-03-16")
// 加算
today.add({ months: 1, days: 5 })
// → "2026-04-21"
// 減算
today.subtract({ weeks: 2 })
// → "2026-03-02"
// 2つの日付の差を取得(Durationが返る)
const start = Temporal.PlainDate.from("2026-01-01")
const diff = start.until(today, { largestUnit: "month" })
// → P2M15D(2ヶ月15日)
// since は逆方向
today.since(start, { largestUnit: "month" })
// → P2M15D(同じ結果)until/sinceで期間を取得するとき、largestUnitを指定しないとデフォルトでは最大の単位が型によって異なります。
明示的に指定することで結果の表現をコントロールできます。
const a = Temporal.PlainDate.from("2025-01-01")
const b = Temporal.PlainDate.from("2026-03-16")
a.until(b) // → P440D(デフォルトはday単位)
a.until(b, { largestUnit: "year" })
// → P1Y2M15D(1年2ヶ月15日)
a.until(b, { largestUnit: "month" })
// → P14M15D(14ヶ月15日)Durationの繰り上げ/切り捨ての計算 roundメソッド
Durationは繰り上げ/切り捨てを計算できます。
ただし、月や年を含む丸めにはカレンダー上の基準日(relativeTo)が必要です。
const d = Temporal.Duration.from({ hours: 7, minutes: 43 })
d.round({ smallestUnit: "hour", roundingMode: "ceil" })
// → PT8H
d.round({ largestUnit: "day", smallestUnit: "hour" })
// → PT8H
// 月・年を含む場合は relativeTo が必須
const long = Temporal.Duration.from({ months: 15, days: 20 })
long.round({
largestUnit: "year",
relativeTo: Temporal.PlainDate.from("2026-01-01"),
})
// → P1Y4M(relativeToを起点に計算)比較 compareメソッド
Durationは比較できます。
const d1 = Temporal.Duration.from({ months: 1 })
const d2 = Temporal.Duration.from({ days: 30 })
// 「1ヶ月」と「30日」、どちらが長い?
// → カレンダー上の起点によって変わる!
Temporal.Duration.compare(d1, d2, {
relativeTo: Temporal.PlainDate.from("2026-02-01"),
})
// → -1(2月は28日なので、1ヶ月 < 30日)
Temporal.Duration.compare(d1, d2, {
relativeTo: Temporal.PlainDate.from("2026-03-01"),
})
// → 1(3月は31日なので、1ヶ月 > 30日)単位変換 totalメソッド
特定の単位に変換した数値が欲しいときはtotalを使います。
const d = Temporal.Duration.from({ hours: 1, minutes: 30 })
d.total("minutes") // → 90
d.total("seconds") // → 5400符号 signメソッド
Durationは正または負のいずれかです。
すべてのフィールドが同じ符号を持つ必要があり、混在はできません。
signプロパティで確認できます。
const pos = Temporal.Duration.from({ hours: 2 })
pos.sign // → 1
const neg = Temporal.Duration.from({ hours: -2 })
neg.sign // → -1
neg.negated() // → PT2H(正に反転)
neg.abs() // → PT2H(絶対値)Durationのまとめ
Durationの設計で特に重要なのは、「1ヶ月」のような曖昧な単位をそのまま保持し、実際の計算時にrelativeToで文脈を与えるという考え方です。
Dateオブジェクトでは、こうした曖昧さが暗黙的に処理されてバグの原因になっていましたが、Temporalでは明示的に扱うことで安全性を確保しています。
さいごに
今回はTemporalについて紹介しました。
主要ブラウザの対応が完了したため、今後はTemporalを使用することが増えていくと思います。
以前までDateオブジェクト・ライブラリを使用していた箇所もJavaScriptの標準APIであるTemporalに書き換わっていくことでしょう。




