6 ポイント 投稿者 GN⁺ 2026-01-13 | 1件のコメント | WhatsAppで共有
  • JavaScriptの既存の Dateオブジェクトの不完全さと非一貫性 を指摘し、それに代わる Temporal API を紹介
  • Dateは ミュータブルオブジェクト(mutable object) として動作するため、実際の日付という概念とずれがあり、パースエラー・タイムゾーン処理の限界 など構造的な問題がある
  • Temporalは イミュータブル性を基盤とした新しい日付・時刻処理モデル を提供し、PlainDate, ZonedDateTime, Duration など細分化されたクラスを含む
  • Temporalのメソッドは既存のオブジェクトを変更せず 新しいオブジェクトを返す ため、明確で安全な チェーン演算 が可能
  • Temporalは現在 標準化ステージ 3(Stage 3) にあり、ChromeやFirefoxなどの最新ブラウザで実験的にサポートされている

JavaScriptのDateオブジェクトの問題点

  • Date コンストラクタは 不一致なパース規則直感的でないインデックス により混乱を招く
    • 例: 月(month)は0から始まる一方、日(day)と年(year)は1から始まる
    • 文字列 "99" は1999年として、"100" は0100年として解釈されるなど、一貫性に欠ける
  • Date時間(time) を中心に設計されており、内部的には Unixタイムスタンプ(ミリ秒単位) として保存される
  • タイムゾーン(time zone) のサポートが限定的で、夏時間(DST)グレゴリオ暦以外 を認識できない
  • こうした限界のため、Moment.jsdate-fns などの大規模なサードパーティライブラリへの依存が一般的であり、これは パフォーマンス低下 につながる

イミュータブル性と参照概念の衝突

  • JavaScriptの プリミティブ値(primitive) はイミュータブルで値そのものとして保存されるが、オブジェクト(object) は参照(reference)として保存されるため変更可能
  • Dateコンストラクタ(constructor) を通じて作られるオブジェクトなので ミュータブル である
    • 例: setMonth()setDate() を呼ぶと元のオブジェクトが直接変更される
  • そのため、同じオブジェクトを参照する変数間で 予期しない値の変更 が発生する
    • 例: today を引数として渡した関数が内部で日付を修正すると、元の today も変更される

Temporal: 新しい日付・時刻API

  • Temporalコンストラクタではなく名前空間オブジェクト(namespace object) であり、Math に似た構造を持つ
    • 主な構成要素: PlainDate, PlainDateTime, PlainTime, ZonedDateTime, Duration, Now など
  • Temporal.Now は現在時点をさまざまな形で返す
    • plainDateISO() → ISO形式の日付
    • zonedDateTimeISO() → タイムゾーンを含む時刻
  • Temporalオブジェクトは 明確なメソッド体系 を提供する
    • add({ days: 1 }), subtract({ years: 2 }) などにより 明示的な単位演算 を実行
    • 既存オブジェクトを変更せず 新しいオブジェクトを返す ことで、イミュータブル性を維持する

Temporalの動作方式と利点

  • Temporalオブジェクトは依然としてオブジェクト型だが、意図されたイミュータブルな利用パターン に従う
    • 例: today.add({ days: 1 }) は新しい日付オブジェクトを返し、元の today は変更されない
  • Date と比べて 簡潔で明確な構文 を提供
    • 例:
      const today = Temporal.Now.plainDateISO();  
      console.log(`Tomorrow will be ${ today.add({ days: 1 }) }. Today is ${ today }.`);  
      // 結果: Tomorrow will be 2026-01-01. Today is 2025-12-31.  
      
  • タイムゾーン指定期間計算ISOフォーマット維持 など現代的な要件に合致する
  • add, subtract, since, until などのメソッドチェーンにより 複雑な日付計算 を簡潔に表現できる

標準化の現状と今後の展望

  • TemporalECMAScript提案ステージ 3(Stage 3) に到達しており、ブラウザ実装が推奨される段階にある
  • ChromeFirefox ではすでに実験的サポートが始まっており、他のブラウザでも導入予定
  • 開発者は現在から テストとフィードバック提供 を通じて仕様改善に参加できる
  • Date は今後も残るだろうが、将来的には Temporalが標準的な日付処理方式 として定着する見込み
  • 記事は「1995年に置き換えられているべきだったが、今からでも Temporal.Now は最適なタイミングだ」と締めくくっている

1件のコメント

 
GN⁺ 2026-01-13
Hacker Newsの意見
  • この記事は、JavaScript の Date コンストラクタが持つさまざまな奇妙な挙動を扱っている
    特に 'YYYY-MM-DD' 形式が UTC の深夜0時として解釈されるため、ローカルタイムゾーンでは日付が1日ずれてしまう問題を説明している
    本来 ISO 8601 では、タイムゾーン指定がない場合はローカル時間として扱うべきだが、ES5 の仕様策定中のミスで “Z”(UTC) として処理されるようになった
    その後 ES2015 で修正しようとしたものの、非常に多くの Web サイトが既存の誤った挙動に依存していたため、Web 互換性を理由に差し戻された
    詳細は Broken Parser セクション を参照

    • 'use strict' のような 'strict datetime' ディレクティブがあればよかったのでは、という意見
      そうすれば既存コードとの 非互換性問題なしに、正しい挙動を選択的に適用できたはず
      あるいは import Date from 'browser:date' のように、内部モジュールとして 修正済みのグローバルオブジェクトを読み込む方式も考えられたはず
    • 自分も以前、文字列を手で分割して タイムゾーンのない日付オブジェクトを作っていた
      誕生日のように単に日付だけを意味する値が、タイムゾーンのせいで変わるのはおかしい
      昔 Outlook が誕生日をタイムゾーン付きで保存していたせいで、国を移動するたびに誕生日が1日ずれていたのを覚えている
    • 「Web 互換性の祭壇に捧げられた」という表現が印象的
      ただ、代案があったのか? 昔の IE5 時代のように、ブラウザのバージョンごとの分岐処理を強制する方がもっと悪かったかもしれない
    • jsdate.wtf を見ると、JS Date の 奇怪な挙動を直接体験できる
    • こうした問題が無数の 小さなバグの原因になっていると考えると、笑えるようで悲しい話だ
  • Rails と Ruby の 時間処理のやり方が本当にうらやましい
    Time.current.in_time_zone('America/Los_Angeles') + 3.days - 4.months + 1.hour のような API は直感的で強力だ
    Ruby は Time オブジェクトを 一貫した単一のオブジェクトとしてオーバーロードしており、変換やキャストに悩むことがほとんどない
    JS でも new Date().add({ days: 1 }) のように簡単に書けたらどれほどよいかと思う

    • ただし、「3.days - 4.months + 1.hour」のような文法が本当に良いのかには疑問を呈している
      また、コアライブラリをオーバーロードするのが本当に良いアプローチかどうかも議論の余地がある
  • Safari がまだ Temporal API をサポートしていないのが残念
    来年くらいには対応してほしい

  • JavaScript の Date には多くの問題があるが、オブジェクトであること自体は大きな問題ではないと思う
    不変オブジェクトであってほしかったが、可変オブジェクトが変更されること自体は驚くことではない

    • ただし、他のコードが自分の持っていた Date オブジェクトを 暗黙的に変更できてしまう点が問題だ
      可変性の本当の危険はローカルではなく、非局所的な変更にある
  • Temporal API が うるう秒 (leap second) の情報をまったく扱わないのが不便
    天文計算向けの JS ツールを作りたいが、UTC 変換にはうるう秒データが必要になる
    temporal-tai のような回避策はあるものの、クライアント側でうるう秒ファイルを維持しなければならず面倒だ
    SOP(CORS ポリシー) のせいで外部サイトからファイルを直接取得することもできない
    ブラウザは定期的に更新されるのに、なぜうるう秒情報を内蔵しないのか疑問だ

    • SOP がうるう秒ファイルを妨げるというのは誤解だ
      サーバー側で Access-Control-Allow-Origin ヘッダーを設定するか、JS ファイルの形で提供すれば可能だ
      ただし、ブラウザが独自にうるう秒データを含めて維持するのは コストの高い作業かもしれない
    • 「UTC だけを扱う」という表現は不正確だ
      UTC は本来うるう秒を含む時間尺度なので、実際には POSIX 時間だけを扱っていると見るのが正しい
  • コード例では "33" ではなく "50" から1900年代として扱うべきだ、という指摘 — 単なる 誤字の指摘

  • Temporal polyfill を使っているが、今のところ非常に満足している

    • ただしサイズが 51KB あるので、軽量とは言い難い (bundlephobia リンク)
      サーバーや大規模アプリには問題ないが、小さなアプリには負担になるかもしれない
    • moment や luxon と比べてどうなのか気になる、という声もある
  • 記事が妙なことに Date.now() にまったく触れていない
    Temporal と比較するなら Date.now() を基準に説明すべきだった
    この関数は 1970年1月1日からの ミリ秒単位の経過時間を返す
    Temporal の方が親切な API ではあるが、根本的には時間の 相対的な距離を表現するのが目的だ

    • ただし、記事で触れられている タイムスタンプ関連のバグも存在する
      では Date を経由せずに、望む形式へ変換するにはどうすればよいのか気になる
  • “daylight savings time” ではなく “daylight saving time” が正しい、という 細かいが重要な訂正

  • これまで JS Date がここまでひどいものだとは知らなかった

    • さらに残念なのは、こうした問題が 1995年の時点ですでにはっきり見えていたことだ
      あの時もう少し慎重であれば、多くの開発者がこうした落とし穴にはまらずに済んだはずだ