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

自分で日付解析ライブラリを書くな

概要

  • Eleventyの 日付パース ライブラリ選定の経緯と課題
  • Luxon依存 からの脱却とバンドルサイズ削減
  • RFC 9557対応の 新ライブラリ @11ty/parse-date-strings導入
  • 他の主要な日付ライブラリとの 比較検証
  • 今後の Temporal API 対応を見据えた設計

Eleventyの日付パースライブラリ刷新の経緯

  • 2018年に Luxon をEleventyの日付パース用ライブラリとして採用
  • Luxonは Node.js専用要件 には適していたが、クライアントサイド対応やバンドルサイズが課題
  • Eleventyの依存モジュール21.3MBのうち、Luxonが 4.7MB(22%) を占有
  • 新しい@11ty/clientバンドルでもLuxonは 229kB/806kB(28%) と大きな割合
  • EleventyでのLuxon利用範囲は DateTime.fromISO によるISO 8601パースのみ

代替ライブラリの検討

  • バンドルサイズ・精度を重視し dayjs などを検証
    • dayjsは小型だが テスト228件中80件で不合格、精度に難あり
  • 他の候補(moment, date-fns)も総合的に不採用
  • Dual publishing (ESM/CJS両対応)の必要性にも疑問

パース仕様の見直しとRFC 9557対応

  • 多様な日付形式対応による 曖昧さ がトラブルの元
  • ISO 8601標準は サブフォーマットが多く複雑
    • 例:"200"は年200ではなく、2000-2010年のディケードを指す
  • 今後は 厳格なパース仕様 に変更(設定でLuxon利用も可)
  • 新しい指針として RFC 9557 (ISO 8601/RFC 3339拡張)を採用
    • Temporal API(次世代JavaScript日付API)とも互換

主要日付フォーマット対応状況一覧

  • RFC 9557, Luxon, ISO 8601, Date.parseの 各フォーマット対応状況
    • 例:YYYY-MM-DDTHH:II:SS.SSS、ミリ秒・マイクロ秒・ナノ秒精度など
    • RFC 9557が最も 幅広い形式 に対応

新ライブラリ@11ty/parse-date-stringsの導入

  • RFC 9557互換の パース専用小型ライブラリ
  • ESM形式、6.69kB(バンドル2.3kB) と超軽量
  • @11ty/clientバンドルで約 230kB削減、@11ty/eleventyのnode_modulesも 21.3MB→16.6MB に減量
  • 今後の Temporal API移行 もスムーズに

他の注目日付ライブラリ・Temporalポリフィル

  • @js-temporal/polyfill:2.98MB(186.5kB min)
  • temporal-polyfill:551kB(56.3kB min)
  • @formkit/tempo:501kB(17.3kB min)
    • それぞれ特徴と用途に応じて選択肢

まとめ

  • Eleventyでは 日付パースの精度・軽量化・将来性 を重視し、独自ライブラリへ移行
  • RFC 9557および Temporal API時代 を見据えた設計
  • 他プロジェクトでも 日付パース要件 の見直しやライブラリ選定の参考事例

Hackerたちの意見

最近、医療の現場で誕生日を保存するために日付の扱いについて悩んだんだ。最終的には、誕生日って物理的な時間じゃなくて、ただの文字列だって考えることにした。ユーザーには「02/18/1993」みたいに先頭のゼロも含めて指定されたフォーマットで入力させることができるし、文字列の等価性以外の操作は無効にするつもり。これが実際にどうなるかはわからないけど、時間の点や区間として扱うよりはずっと良くなってる。タイムゾーンを移動したときに誕生日が変わる問題もないしね。

人々の誕生日がタイムゾーンを移動すると変わるのは、開発者が単一の日付を保存するのに日時(タイムスタンプ)を使っているからだよ。適当な基準日(例えば、Excelで使われている1900年1月1日とか、僕のお気に入りの1600年1月1日。1600は400の倍数だから、うるう年の計算がさらに簡単になる)を選んで、そこから経過した日数を保存すればいい。うるう年に関するルールは、タイムゾーンやタイムゾーンデータベースに関するルールよりずっとシンプルだし。この表現から年月日への変換は、コードで約50行あればできるよ。もちろん、日付の計算をしないなら、年、月、日をそれぞれ3つの数字で保存すればいいだけ。

さらに良いのは、ユーザーにテキストフィールドの代わりにカレンダー日付ピッカーを使わせることだね。これなら入力を完全にコントロールできる。

「02/18/1993」のフォーマットは、DD/MM/YYYYなのかMM/DD/YYYYなのか?18から判断すると後者だと思うけど、その規則は普遍的じゃないよね。あまりあいまいじゃないフォーマットとしてYYYY/MM/DDをおすすめするけど、完璧な答えはないな。

あなたの場合、日付の計算や大小比較はやらずに、等価性のテストだけをしてるんだよね?つまり、複合キーの一部ってことだ。僕も似たような問題に直面したことがあって、ユーザーが日付を提出するフォームがあったんだけど、タイムゾーンについてはあまり意識してなかったみたい。だから、「02/28/1993」を選んで、みんなが常に「02/28/1993」を見る限りはそれが正しいと考えたし、もし違うのを見たらそれは間違いだと思った。だから、システム全体でタイムゾーンを意識しない日付を使ったんだ。

Temporal[0]がこれをやるのが好きだな。君が扱ってたのはTemporal.PlainDate[1]、つまりカレンダーはあるけど時間やタイムゾーンはない日付だね(暗黙のうちに含まれてるかもしれないけど、誕生日みたいに関係ないこともある)。Temporalには他にも面白いタイプがあって、それぞれ異なる意味があるんだ:- Instant: カレンダーや場所に関係なく固定された時点。例えば「ユーザーがX日付と時間にログインした」みたいな感じで、どのタイムゾーンやカレンダーシステムでも有効。これが通常「Unix UTCタイムスタンプ」で使われるもの。- ZonedDateTime: Instantに似てるけど、特定のカレンダーと場所に関連付けられてる。Instantをカレンダーシステムとタイムゾーンに「リアル」に変換した感じで、ユーザーが意味のある時間を見れるようになってる。- PlainDate: さっき話したやつ。例えば誕生日とか。- PlainTime: 「毎日6:30pmにタスクを実行する」みたいな感じ。- PlainDateTime: Instantに似てるけどカレンダーシステムに関連付けられてて、タイムゾーンはない。ユーザーが日付ピッカーに入れるようなもので、タイムゾーンは暗黙的に含まれてる。- PlainYearMonth: 例えば「2025年10月にレポートを実行する」みたいな感じ。- PlainMonthDay: 例えば「私の誕生日は6月13日」みたいな。- Duration: 例えば「タスクは3時間30分実行された」みたいな。重要な概念も見てみてね[2]。[0] https://tc39.es/proposal-temporal/docs/ [1] https://tc39.es/proposal-temporal/docs/#Temporal-PlainDate [2] https://tc39.es/proposal-temporal/docs/timezone.html

誕生日を扱う環境ってどんなとこなの?タイムゾーンを意識した日付や時間、間隔があるのに、すでに存在するナイーブ/プレーン/ローカルな日付タイプがなくて、文字列を使わざるを得ないって?その問題に対する合理的な解決策があるみたいだけど、そんな問題が最初にあるってのが意外だね。

通常の「法的」な日付はタイムスタンプやタイムゾーンに関連してないし、この事実は人間がソフトウェアを書く限り永遠に再発見されるだろうね。

医療がなぜ重要なのかを理解しているなら、これは本当に洞察に満ちてるね。私の知る限り、医療業界は基本的に誕生日をキーとして使ってる。これによって(a)他のプライマリーキー(名前や住所など)を持つ2人の患者を区別できたり、(b)患者にその値を言わせて、簡単に認知能力をチェックしたりできるんだ。

ただの文字列として保存すると、年齢に依存するロジックとか、フィールドのユーザーに解析ロジックを渡すことができなくなるよ。個人的には、FHIRは日付(生年月日を含む)に関してかなりいいシステムを持ってると思う:YYYY、YYYY-MM、またはYYYY-MM-DD。(正確な誕生日を知らないのは、国によってはよくあることだしね)。 https://build.fhir.org/datatypes.html#date

誰かがTwitterに投稿したジョークみたいだね。「宇宙探査には賛成だったけど、日付/時間ライブラリに何を意味するかを理解してからは考えが変わった。」

時計の数字が特定の数になったら寝なきゃいけない理由を考えるのって面白いよね。現代の技術なら、必要に応じて調整できるようにプログラムできるのに。火星人が今日の9:00amに寝て、明日の9:40amに起きることに技術的な理由はないし。農家がサマータイムを気にするのが滑稽だと思うのも同じで、知ってる農家は「夜明け」や「日没」の時間を基準にしてるからね。

Andorで「日」や「月」、「年」って言葉が出るたびに、銀河全体で意味が通じないことを考えないように脳をリセットしなきゃいけなかったよ。

その問題は今も存在してるけど、みんな頭を砂の中に突っ込んでるね。

昔、すべての日付をYYYYMMDD形式の整数で保存している会社で働いてたんだ。なんでそうしてるのか聞いたら、2つの日付を引き算して差を出せるからって言われた。SQLデータベースなのにDATEDIFFを使えない理由を聞いたら、聞いたことがないって言われて、新しい機能だと思ってるみたいだった。

え、じゃあ新年の一日は2025-01-01 - 2024-12-31 = 20250101 - 20241231 = 8870ってこと?つまり90ヶ月と10日、または7年6ヶ月と10日ってことだよね。それがどうして一日と同じなの?

メインフレームでは、ジュリアン日付がその理由で人気なんだ。YYDDD(年の日)。今日から30日後はいつ?25206+30

日本のカレンダーを例にして、後輩たちをDIYパースから遠ざけるのが好きなんだ: https://learn.microsoft.com/en-us/dotnet/api/system.globaliz... https://learn.microsoft.com/en-us/windows/apps/design/global...

これに関しては特に問題ないと思うよ。むしろ面白いチャレンジだし、みんなに日本のカレンダー形式を理解してほしいな。知ってる人が多い方がいいからね!

ユーザーがそんな日付を入力するの?しない?じゃあ問題解決だね。DIYパースの利点は、ユーザーが扱いたいと思う入力のセットに制限して問題をシンプルにすることであって、一般的なライブラリを作ることじゃない。ジュニアにとっての正しい教訓は、物事を過剰に複雑にしないことだよ。

ISO8601のパーサーにこれを入れてみたけど、全然ダメだった。ISOにちゃんとした解決策を作るんじゃなくて、DIYで済ませてることを文句言ってやるつもり。

時間やタイムゾーンを扱うことほど、私にとって苦痛をもたらしたプログラミングの概念はないよ。深く考え始めると、ほんとに頭が混乱するほど複雑になってくる。しかも、年によってタイムゾーンが変わる場所もあるから、これに遭遇する前からもう大変なんだ。教訓としては、ライブラリを選ぶべき(momentは最高だよ)で、もう時間のことは考えない方がいい。

歴史的に体系的じゃないからだよ。エッジケースがいっぱいあるシステムなんだ。

残念ながら、業界の多くの人はタイムゾーン管理の一般的な落とし穴を理解してない。要件を設定する人たちも含めてね。「ユーザーのために毎週平日の午前2時に実行したい」と言って、単にそれをUTCオフセットとして保存して終わりにするのが典型的なパターン(ダジャレも含めて)。

タイムゾーンについて覚えておくべき本当に重要なことは、数学的な構造でも物理的な構造でも天文学的な構造でもないってこと。政治的な構造なんだ。その視点で見ると、自分のコードが実行される国の法律に依存する部分があるのと同じくらいの複雑さがあるよ。

「momentは素晴らしい」以外は全部同意だよ。開発者たちももう使うなって言ってるし、偶発的に変わった日付が原因でバグが出たアプリも何度も見てきた。Luxonはいいけどね。

でも、やっぱり時間について考えないといけないよね… ライブラリは本当に厄介な詳細を手助けしてくれるけど、高レベルな要件にもたくさんの落とし穴がある。例えば、スケジューリングアプリに関わったことがある人なら誰でも知ってる「今日のイベントのリストを取得する」ってやつ。で、「今日」って誰の「今日」?(タイムゾーン)サーバー、クライアント、ユーザーアカウントの設定?それとも、イベントの物理的な場所のタイムゾーン?そもそも「今日」って何を意味するの?日付を切り捨てるの?00:00-23:59?営業時間?「今日のイベント」って何を指すの?イベントは真夜中を跨ぐこともあるし… イベントは今日始まる必要があるの?今日終わる必要があるの?両方?イベントは複数の日に跨がることもある?楽しみは終わらないね!

ライブラリでは、タイムゾーンの厄介な部分を簡単にすることはできないよ。ほとんどの日付問題には「正しい答え」がたくさんあるし、それぞれの文脈によって求められるものが違うから、ライブラリは多くのことに対して意見を持てない。時間を扱うコードを書くなら、時間の仕組みを理解する必要があるよ。

特に、99.9999%の時間、特別なケースなんて全然気にしないし、ただユーザーにシンプルな日付を見せたいだけなんだよね。それでも、APIやサードパーティが変なことをやらかすせいで、問題にぶつかることがよくある。

moment.jsは、もう5年近く前から非推奨になってるよね?現代の代替品の中から選ぶのはおすすめしないな。

「自分で実装するな」って見るたびに、自分で実装したくなるんだよね。みんな難しいことについてそう言うけど、私は難しいことだけやりたい。簡単なことができる人なんて誰も求めてない、みんな難しいことができる人を求めてる。難しいことを学ぶ唯一の方法は、難しいことをやることだから、最も難しいことをやってみて。だから、自分のデートライブラリやUnicodeフォントレンダリング、コンパイラ、OS、ゲームエンジンなど、みんなが「やるな」って言うことをやってみなよ。

ぜひ書いてみて。けど、使わないでね。この警告は、ほとんどの場合、リリースするコードの文脈での話で、自分で学ぶための練習じゃないから。

難しいことをやりたい気持ちは分かるけど、バイナリで書くの?自分で電気を作るの?俺にとって一番大事な資源は時間なんだ。確かに、もっと低レベルな技術を学ぶこともできるし、時々それが役に立つこともある。でも、すでに解決された難しいことを自分流に再実装することに集中しても、何の価値を加えてるの?クライアントでライブラリやコードの書き方に興味を持ってる人には会ったことがないよ。壊れるまではね。壊れたら、誰が書いたかなんて気にせず、ただ動き出すことだけを気にするんだ。

こんなに物議を醸す意見だとは信じられない。自分で難しいことを解決するのは成長だよ。俺は100%同意する。新しいJSフレームワークを学ぶよりも、また収益を失うSaaSを立ち上げるよりも、難しい問題を自分で解決する方がいいよ。「成功」ってVCのおかげだし。何でもいいけど、限界を押し広げよう。

難しいことを学ぶ唯一の方法は、難しいことをやることだから、最も難しいことをやってみて。従業員に学ばせるためにお金を払いたくない、売れる成果を出してもらうために払いたいんだ。難しいことをやるのはいいけど、それが今までに誰もやったことがないこと、例えば月に行くことみたいな場合ね。すでに誰かがやったことを、自分がやってないからってやるのは良くないよ。エンターテインメントや自己成長のためならいいけど、それは自分の金でやるべき。エベレストに登ったり、南極に行ったりするのもそう。誰かのためにプロジェクトをやってるなら、個人的な欲望をプロジェクトに持ち込むのはダメだよ。

でも、苦労にはいろんな種類があるよね。結果として、問題の理解の仕方やナビゲートの仕方が変わることもあるし、純粋に同時進行の慣習の蓄積で難しくなることもある。全人類を調和させるためにみんなが幸せに合意するのは難しいからね。日付の扱いは後者にあたる。運よく掘り下げる方向に行けば、宇宙論的な考察にたどり着くこともあるけど、基本的にはカレンダーはめちゃくちゃだよ。

人は難しいことについてそう言うけど、俺は難しいことだけやりたいんだ。それは全然問題ないよ。自分の時間、自分の趣味だし。 > 簡単なことができる人なんて誰も求めてない、難しいことができる人が求められてる。いや、実際はそうじゃない。人は簡単なことができる人を求めてるんだ。無駄に難しいことをやらずに済む賢さがあるからね。君の愚かな考え方が、偶発的な複雑さや遅れたプロジェクトを生んでるんだよ。「賢く働け、ハードに働くな」って言葉もあるし。 > さあ、自分のデータライブラリやUnicodeフォントレンダリング、コンパイラ、OS、ゲームエンジン、他の人が「難しいからやるな」って言うことをやってみなよ。これを切り捨てて、これはLinkedInじゃないから。

このコメントはちょっと不思議だね。リンク先の記事のほとんどが、著者が自分で日付解析ライブラリを作ったことについて書いてるから。「自分でやるな」って部分は最初の数行だけで、正直言ってほとんどジョークみたいなもんだよ。この記事は、自分で日付解析ライブラリを書くなとは言ってないし、要するに「やるな。俺はやった。これが俺のやり方とその理由だ」って感じ。

「自分で実装するな」って見るたびに、自分でやりたくなるんだよね。学ぶためにやるのは有益だし、この一般的なフレーズの意図は、プロダクション環境で難しくて重要なものを「自分で実装するな」ってこと。俺は暗号学の仕事をしてて、学ぶためにいくつかのことを自分で実装してきたけど、実際に使うときは安定していて、フィールドテスト済みの暗号を使ってる。 > 難しいことについてはみんなそう言うけど、俺は難しいことだけやりたい。簡単なことができる人なんて誰も求めてないし、みんな難しいことができる人を求めてる。難しいことだけやりたいってのは、自分をかなり制限しちゃうよ。簡単に見えるけど改善できることはどうするの?俺は一時期、非技術系の医療製造の仕事をしてて、プロセスやツールを学ぶ時間を取った。その後、同僚(プログラミングやITの経験がない人たち)が使えるExcelやラボのコンピュータのVBAを使って、手作業でやってた在庫リストの準備を手伝うツールをいくつか実装したんだ。手作業だとグループ全体で3時間かかってたけど、俺のツールはボタン一つで5秒で終わった。今でも約10年後の今、使われ続けてるよ。これは「難しい」ことじゃなかった。いくつかのファイルをくっつけて、フィルターでリストをグループ化して、列でグループをソートして、読みやすくてマークしやすい印刷物を作っただけ。だけど、同僚たちは誰かが別のスキルセットを持って来るまで、これが可能だってことすら知らなかった。難しいことだけやるのは注意が必要だよ。他の人が何が難しいかを特定する必要があるから!それに、変わり者は難しいことだけやって、今までの解決策よりも良い解決策を見つけたと信じてる(相談したり、何が行われてきたかを学んだりせずに)。面白い人は、物事をそのまま学び(ほとんどのことにおいて専門家ではないという謙虚さを持って)、持っている知識を使って改善しようとする。自分の暗号を作る時間を無駄にするんじゃなくて、実際の「難しい」ことをやって、未解決のスペースを特定して、慎重で考慮された改善を行うべきだよ。

いくつかのことは、面白い分野に基づいた「良い難しさ」だと思う。よく設計されたツールやシステムを使って深く掘り下げたり、たくさんのクールなことを学んだりする。だけど、日付時刻関連のコードはその真逆だと思う。難しい部分は、現実の地理や政治、物理学/天文学に細かく従うことから来てる。そこから健全なモデルを抽出するための一貫性はなくて、特別なケースや恣意的なパラメータが積み重なってるだけ。もちろん挑戦は受け入れるけど、全てが同じなら「悪い難しさ」の仕事は他の人に任せておくのが嬉しいな。

さあ、自分のデートライブラリ、自分のUnicodeフォントレンダリング、コンパイラ、OS、ゲームエンジン、あるいは「難しいからやるな」と言われることをどんどん書いてみて。絶対にできるよ。気をつけるべきなのは、ほとんどの場合、これらをプロダクション品質のレベルで維持するのは才能あるエンジニアのフルタイムの仕事だってこと。だから、もし以下の条件があるなら、これに挑戦するべきじゃない。 - 達成したい高次の目標がある - プロダクション品質の実装が必要 もしそのどちらかが当てはまらないなら、思う存分やってみて。

この記事では、ISO 8601の特別な楽しさ、相対値や非グレゴリオ暦の値、期間などについては触れてないね… 標準の中には驚くべきこともあって、特別なリクエストだったのかもしれない。昔、こんなことをコメントしたことがある。「フランスの田舎のどこかに、古い家族経営のワイナリーを運営している人がいて、その人は今でも樽にタイムポイント情報を刻印している…」その人の恋人がISO 8601の委員会にいたんだ。(昔、SchemeでISO 8601のすべてに対応した時間ライブラリを書いたことがある。解析、表現、印刷、カレンダー変換、算術を含めてね。混合精度や相対値の算術もあった。最初に問題を本当に解決するためのコアライブラリの演習だったんだ。後で手間がかかるような妥協やAPIの破損を避けるためにね。異なるカレンダーシステム間の算術を実装しようとしたかどうかは、ちょっと覚えてないな。)

オープンソースなら、そのSchemeコードを見てみたいな!

2012年にPythonの「datetime」ライブラリにISO 8601の日付パーサーをリクエストしたんだ。[1] 「datetime」はISO 8601にフォーマットできたけど、文字列をパースすることはできなかった。利用可能なISO 8601パーサーは5つあったけど、どれもダメだった。6年の無駄話の末、2018年にやっと修正された。これで自分で日付パーシングライブラリを書く必要がなくなった。[1] https://github.com/python/cpython/issues/60077

これで自分で日付パーシングライブラリを書く必要がなくなった。もし自分で日付パーシングライブラリを書いてたら、6つのISO 8601パーサーがあったけど、どれもダメだっただろうね。時間を無駄にしなかったことに感謝すべきだよ。

私の意見では、ISO 8601という標準は広すぎて具体性がない。ISO 8601はめちゃくちゃだよ。誰かにISO 8601の日付時刻をパースする必要があると言っても、それだけじゃ仕事をするには情報が足りない。どのバリアントなの?時間部分は含まれてるの?私の意見では、データフォーマットでISO 8601の日付と時刻の全範囲を許可するのは通常間違いだと思う。もっと具体的にするべきだよ。一般的に使われるバリアントを固定して、あらゆる曖昧さを取り除く標準が必要だと思う。私にとっては、'YYYY-MM-DDThh:mm:ss.xxxxxZ'というパターンに従ったタイムスタンプだけで十分で、私のAPIはそれだけを受け入れたり生成したりする。UTCに正規化されてない古いシステムが存在するのはいいけど、秒や秒の分数をオプションにするのはどうでもいい。明確なタイムスタンプが欲しいだけなんだ。これに対するパーサーを書くのはかなり簡単だよ。シンプルな正規表現で十分だし、もちろんユニットテストも追加するべき。技術的には、Zは冗長な情報だよね。みんながUTCに正規化することに同意できれば。私の意見では、そうするべきだよ。同様に、T部分や区切りも冗長だけど、人間が読みやすいからいいんだ。広く利用可能なdatetimeライブラリを使って、必要に応じてタイムスタンプをローカライズすることができる。でも、タイムスタンプは正規化されていて100%明確な方法で保存・送信されるべきだよ。無意味で非常に曖昧だけど有効なISO 8601のバリアントをすべてサポートしようとすると、パースが問題になるんだ。実際、どのバリアントが使われているかの知識なしにすべての有効なバリアントをパースできるパーサーなんて存在しないよ。もちろん、主要なバリアントやマイナーバリアントの一部またはすべてをサポートする複雑なAPIを持つライブラリはたくさんあるけど、単に「parse()」という関数だけではないんだ。ISO 8601の主な課題は、このバリアントを使うべき別のものとして明示的に示してこなかったことだと思う。これこそが独自の標準であるべきだし、それを使わないのは間違いだよ。ISO 8601は、委員会によるデザインの結果なんだ。

広く利用可能なdatetimeライブラリを使って、必要に応じてタイムスタンプをローカライズすることができる。でも、タイムスタンプは正規化されていて100%明確な方法で保存・送信されるべきだ。もし「タイムスタンプ」が過去の日付や決定的な未来の日付を意味するなら、同意するよ。(ただし、整数を使って文字列パースのステップを完全にスキップできるように、私はそれにはミリ秒のUnixエポックが好きだけど。)でも、未来の日付を扱う必要がある不運な場合、特に「壁の時計」(「2029年7月26日の午後1時にフランクフルトで会おう」)のような場合、タイムゾーンを知ることはできないよ。理由はいろいろあるけど、特にこの場合、EUがその時までに夏時間を廃止する可能性が高いからね。だから、正確でありたいなら、保存するタイムスタンプに地理的位置を含める必要があるよ。

...「ISO 8601 / RFC 3339」のフォーマットへの拡張として宣伝されている この引用はMDNから来てるけど、いろんな意味で間違ってるよ。1. RFC 3339はフォーマットじゃなくて、メタフォーマットなんだ。インターネットアプリケーションで使われるタイムストリングが持つべき特性を指定してる。あるアプリケーションでは日付と時間を分けるのに「T」を使う必要があるかもしれないし、別のアプリケーションではスペースを使う必要があるかもしれないけど、どちらも3339に準拠してる。2つのアプリケーションから日付と時間を解析するのに同じパーサーを使うのは間違いだよ。「1234-12-12 12:34 ...」は前者では1234年12月12日を明確に表してるけど、後者では同じ日の12:34を表してるから。2. だから、RFC 3339はISO-8601のサブセットじゃない。RFC 3339を満たすISO-8601のサブセットは存在するけど、RFC 3339を満たすがISO-8601のサブセットではないタイムストリングもたくさんある(最も有名なのは「T」をスペースに置き換えることだけど、これはRFC 3339に準拠したアプリケーションが可読性のためにRFCのABNFから逸脱する例に過ぎない)。3. TFAの表と#2から明らかなように、RFC 9557はISO-8601の拡張にはなり得ない。表には有効なISO-8601のタイムストリングが含まれているけど、それは無効なRFC 9557のタイムストリングだから。4. これは小さな指摘だけど、RFC 9557はオフセットに関していくつかのRFC 3339のタイムストリングの意味を変更している。RFC 3339ではオフセットが+00:00と同じであることを示すために「Z」(または「z」)を指定しているが、9557では「Z」が-00:00と同じだ。RFC 9557の本質はRFC 3339にサフィックスタグを追加することだから、9557を3339の拡張と呼ぶことには異論はないよ。1: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...