世界を動かす技術を、日本語で。

Temporal: JavaScriptにおける時間を修正するための9年間の旅

概要

  • Bloomberg はJavaScriptの標準化やインフラに深く関与
  • Date API の歴史的課題とその限界
  • Moment.js などのライブラリ依存による新たな問題
  • Temporal 提案の誕生と発展の経緯
  • Temporal API の特徴と現状

BloombergとJavaScript標準化の歩み

  • Bloomberg はJavaScriptの利用と標準化に積極的に関与
  • 社内エンジニア向けに JavaScript環境 を提供
  • 2018年以降、 TC39 会議に参加し、RealmsやWebAssemblyなどの議論に貢献
  • Igalia との協力による標準化推進
    • Arrow Functions、Async Await、BigInt、Class Fields、Promise.allSettled、Promise.withResolvers、WeakRefs、Source Maps標準化支援
  • Promise.allSettledTemporal 提案への貢献

JavaScriptの進化と標準化プロセス

  • JavaScriptは ブラウザ横断 で動作、単独での仕様変更不可
  • TC39 (ECMAScript技術委員会)で進化
  • 提案は複数の 成熟度ステージ を経て標準化
    • Stage 0: アイデア
    • Stage 1: 問題領域の承認
    • Stage 2: 設計案の選定
    • Stage 2.7: 原則承認、テスト・フィードバック待ち
    • Stage 3: 実装とフィードバック
    • Stage 4: 標準化

Date APIの歴史的背景と課題

  • 1995年、 Brendan Eich がMocha(JavaScriptの前身)を10日で開発
  • JavaのDate実装 をそのまま移植
  • 当時はJavaScriptをJavaの軽量版として設計
  • APIの変更は政治的にも困難、 一貫性優先
  • Webが進化しても、 Date API はほぼ変化なし

Dateの主な問題点

  • ミュータブル (変更可能)なオブジェクト設計
    • 意図せず元のDateを変更してしまう
  • 月単位の計算の一貫性欠如
    • 例:1月31日に1ヶ月加算→3月2日になるなど直感に反する挙動
  • 曖昧なパース
    • 仕様外の文字列で挙動がブラウザごとに異なる
    • ローカルタイム・UTC・エラーのいずれかになる不確定性

ライブラリエラの到来と課題

  • Moment.js などのライブラリがDateの欠点を補完
    • 強力なパース、イミュータブルなAPI、表現力の高い操作
  • 100万回以上の 週次ダウンロード を記録
  • ライブラリ導入で バンドルサイズ肥大 問題
    • ロケール・タイムゾーン情報の同梱不可避
    • Tree-shakingや最適化でも不要データの除去が難しい

Temporal提案と推進体制

  • Maggie Johnson-Pint らが2017年に Temporal Proposal をTC39へ提出
  • Stage 1到達後、要件整理や設計の明確化など地道な作業が続く
  • Bloomberg の要件
    • ユーザーごとのタイムゾーン設定
    • IANA Time Zone Databaseによる正確な歴史的タイムゾーン挙動
    • ナノ秒単位の高精度タイムスタンプ
  • IgaliaGoogleMicrosoft など多様な関係者が協力
  • Champion メンバー:Maggie Johnson-Pint、Matt Johnson-Pint、Brian Terlson、Richard Gibson、Philipp Dunkel、Ujjwal Sharma、Philip Chimento、Jason Williams、Shane Carr、Justin Grant

Temporal APIの概要と特徴

  • Temporal はグローバルスコープのトップレベル名前空間オブジェクト
  • MathやIntl同様、 複数の型 (コンストラクタ)を内包
  • 代表的な型: Temporal.ZonedDateTime
    • Dateの概念的な後継
    • 明示的なタイムゾーンとカレンダーサポート
    • 完全なイミュータブル設計
    • サマータイムなどの複雑な計算も正確に対応
  • 例:現在日時の取得
    • const now = Temporal.Now.zonedDateTimeISO();
  • ZonedDateTime型は日時計算時にタイムゾーンやサマータイムの遷移を考慮
    • 例:ロンドンのDST開始時の加算操作

まとめ

  • Date API の限界を克服するために Temporal が標準化
  • イミュータブル設計高精度サポート明確なタイムゾーン管理
  • 業界横断の協力体制による長年の課題解決
  • JavaScript開発者 にとって、今後の標準的な日時操作手段となる見込み

Hackerたちの意見

サーバーサイドのランタイムに導入されるのが待ちきれない!これがあれば、全面的に採用できるんだけどな。

Node 26!時間の問題だね… :)

参考までに、私はしばらくの間、js-temporalポリフィルを使ってサーバーサイドで使ってるけど、問題はないよ。

Denoはもうかなりのマイナーバージョンで--untable-temporalフラグの裏にあったけど、最新のマイナーアップデート(TC-39のステージ4受理とV8自体がAPIを安定版としてマークしたから)でフラグの要件がなくなって、すぐに使えるようになったよ。

Temporalで最も評価されていないデザインの決定は、すべてを不変にしたことだと思う。これまでにデバッグした日付関連のバグの半分は、共有されたDateオブジェクトに対してsetMonth()やsetHours()を呼び出したことから来てる。残りの半分は、Dateコンストラクタでの暗黙のローカルタイムゾーン変換が原因。Temporalは、ZonedDateTimeとPlainDateを使ってタイムゾーンを明示的に扱わせることで両方を解決し、新しいオブジェクトを返すことで変更を避けている。9年は長いけど、TC39の提案が半端な状態で出てきて、その後に修正提案が必要になることが多いから、これだけはちゃんとやってほしいな。

それ、実はTemporalで最も評価されているデザインの決定かもしれないね ;) どちらにしても、私も大ファンだよ。

不変性は一般的に過小評価されてるよね。非クロージャーのコードを扱うたびに痛感するよ。

最悪なのは、値を変更して返すメソッドだね。これはコンピュータサイエンスの複雑な領域に入るから、あんまり理解できてないけど、TypeScriptで「この関数に渡されたオブジェクトは今や型が_never_です。壊しちゃったから、これ以降は使えません」って定義できたらいいのに。便利さとパフォーマンスの理由から、関数内で何かを変更して返したいこともあるけど、返された型について考えさせて、元の型には二度と触れないようにしたいんだ。たとえ同じオブジェクトでもね。

これ、10年以上前にJavaで時間を革命的に変えたJoda Timeから取ったみたいだね。残念ながらJodaのことには触れられてないけど。

他の半分は、Dateコンストラクタでの暗黙のローカルタイムゾーン変換から来てる。古いC++(だと思う)バージョンでもその問題を見てみて。ロンドンにいるときに友達の誕生日を3月11日として保存したとする。今はSFにいるけど、友達の誕生日はいつ?それはまだ終日3月11日で、3月10日の午後5時から始まって、3月11日の午後5時に終わるわけじゃない。

JavaScriptを書くときは、できるだけ多くのものを不変にするようにしてる。時々冗長になって、効率的な計算パターンが減ることもあるけど、全体的には理解しづらいバグに直面することがずっと少なくなると思ってる。Temporalのデザインにはあまり好きじゃないところもあるけど、不変性は良い判断だった。理解できないのは、なぜ文字列フォーマットをそんなに厳格にしたのかってこと。国際化に関係してるのかな?レンダリングされた日付・時間の文字列をもっと簡単に作れるようなテンプレートシステムがあったらよかったな。

C++ARM時代にC++で最初に感謝するようになったことの一つは、可変性をモデル化する能力だった。もちろん、他の言語はそれをもっと上手くやってるけど、問題はまだ広まっていないことだね。

Hacker Newsで議論の続きを見る