3 ポイント 投稿者 GN⁺ 2023-09-01 | 1件のコメント | WhatsAppで共有
  • 日付・時刻表記を扱う際、RFC 3339 は Web・インターネットで使いやすい狭い部分集合に近く、ISO 8601-1:2019 ははるかに広い形式集合を含む
  • 比較範囲は ISO 8601-1:2019 に限定され、ISO 8601-2:2019 の季節、集合、不確実性修飾、日付演算のような追加表現は表にまだ反映されていない
  • 両標準は 2026-06-2614:08:00Z2026-06-26T14:08:00Z+00:00 オフセットのような広く使われる 基本的な日付・時刻形式 を共に扱う
  • ISO 8601 は世紀・十年・通算日・週日付、短縮時刻、カンマ小数点、期間(P1Y)と範囲(2026-06-26/P1Y)まで包含するが、RFC 3339 では表の大半が除外される
  • Date-Time 表記では T 区切り文字 と大文字小文字、-00:00 オフセットのような差異が実際のパーサ互換性を分けるポイントになる

比較範囲と前提

  • 形式表は 完全な一覧ではない
  • 対象標準は ISO 8601-1:2019
    • 以前の版と草案には重要な差異がある
  • ISO 8601-2:2019 は追加表現を含むが、このページにはまだ反映されていない
    • 下位年グループ、たとえば季節
    • グループ化単位
    • 集合
    • 不確実性修飾
    • 日付演算
  • RFC 3339 は下位標準で T を別の文字に置き換えられると提案しているが、例としては 空白文字 しか示していない
  • 各標準は目的別の形式を定義しており、それ以外の形式は推奨されない

日付表記では ISO 8601 のほうが広い

  • RFC 3339 と ISO 8601 はどちらも 2026-06-26 のような 年-月-日の日付 をサポートする
  • ISO 8601 は RFC 3339 より多様な日付表現を扱う
    • 世紀: 20
    • 十年: 202
    • 年: 2026
    • 年月: 2026-06
    • 通算日: 2026-177
    • 週日付: 2026-W26, 2026-W26-5
    • 基本形式: 20260626, 2026177, 2026W26, 2026W265
  • 表では RFC 3339 は上記の ISO 専用日付形式を許可していない

時刻表記の違い

  • 両標準は 14:08:00Z14:08:00+00:0014:08:00.372+00:00 のような 秒単位の時刻とタイムゾーンオフセット を共に許可する
  • RFC 3339 は大文字小文字を区別しないため、TZ をそれぞれ tz と書ける
    • 以前の ISO 8601 版も大文字小文字を区別しなかった
  • ISO 8601 では最小の時間値に 小数部 を付けられる
    • 表では主に小数1桁の例が示されているが、標準では任意精度を許可する
    • 小数区切りとしてカンマとピリオドの両方を許可し、すべての形式で相互に置き換え可能
  • ISO 8601-1:2019 は曖昧でない場合、時刻単独表現T の省略を許可する
  • ISO 8601 は 1414:0814:08:00140800T14:08:0014:08:00,372 のような短縮・基本・カンマ小数の時刻表現をサポートする
  • RFC 3339 は 14:08:00-00:00 のような -00:00 オフセット を許可するが、表では ISO 8601 はこれを許可していない

Date-Time における T と区切り文字

  • RFC 3339 と ISO 8601 はどちらも 2026-06-26T14:08:00Z2026-06-26T14:08:00+00:00 のような Date-Time を許可する
  • ISO 8601 の Date-Time 表現 では T が常に必要
    • 以前の版では Date-Time での T 省略も許可されていた
    • 以前の版でも空白やアンダースコアのような代替文字の挿入は許可されていなかった
  • 表では RFC 3339 は次の Date-Time 変種を許可する
    • 小文字 tz: 2026-06-26t14:08:00z
    • 空白区切り: 2026-06-26 14:08:00Z
    • アンダースコア区切り: 2026-06-26_14:08:00Z
    • -00:00 オフセット: 2026-06-26T14:08:00-00:00
  • ISO 8601 は 2026-06-26T142026-06-26T14:082026-06-26T14:08:002026-177T14:082026-W26-5T14:08 のような 短縮 Date-Time と通算日・週日付ベースの Date-Time を扱う

期間と範囲は ISO 8601 中心

  • 表では 期間(Periods) 形式は ISO 8601 にのみチェックが付いている
    • 例: P1Y, P1M, P1W, P1D
    • 時間を含む例: PT1H, PT1M, PT1S
    • 組み合わせ例: P1Y1M1DT1H1M1S
    • 小数例: P1.5Y, P1,5W, PT1.5S
  • 範囲(Ranges) 形式も ISO 8601 にのみチェックが付いている
    • 日付と期間: 2026-06-26/P1Y
    • 日付と日付: 2026-06-26/2026-06-26
    • 期間と日付: P1Y/2026-06-26
    • Date-Time と期間: 2026-06-26T14:08/P1DT1H
    • 反復範囲: R/2026-06-26/P1Y, R10/2026-06-26/P1Y

形式キーとテストツール

  • 形式表は %Y%M%D%h%m%s のような 形式キー を使用する
    • %Y: Year
    • %M: Month
    • %D: Day
    • %V: Week Year
    • %W: Week
    • %w: Week Day
    • %O: Ordinal Day
    • %h: Hour
    • %m: Minute
    • %s: Second
    • %u: Microsecond
    • %n: Nanosecond
    • %Z: + または - を含むタイムゾーン時
    • %z: タイムゾーン分
  • 形式チェッカーは入力形式が表内の形式に該当するかどうかだけを確認する
    • すべての可能な形式を検査するわけではない
  • ISO 8601 as a Service はベータテスト用サービス
    • 現在は DateTimeDateTime のみサポート
    • PeriodRange はサポートしない
  • ソースは GitHub で公開されている

1件のコメント

 
GN⁺ 2023-09-01
Hacker News のコメント
  • 特定のタイムゾーンに基づく将来の日付/時刻を指定する方法がないのは奇妙です。たとえば、2030年7月1日のロンドン現地時間午後6時に会議を設定したい場合があり、その間に英国のタイムゾーン規則がどう変わっても「ロンドンの午後6時」のままであるべきです
    英国は現在、おおむね11〜3月は Z+00:00、4〜10月は夏時間 Z+01:00 を使っていますが[0]、2030年までに中央ヨーロッパ時間[1]を採用したり、British Double Summer Time[2]を再び試したり、夏時間を廃止したりする可能性もあります。そのため、同じ「午後6時」が特定の epoch 基準では大きく変わり得ます
    カレンダーイベントに「その時点のロンドン時間で午後6時」を入れたいのに、2030-07-01 18:00:00 Europe/London を標準的で相互運用可能な方法で表現する手段がありません
    [0] https://en.wikipedia.org/wiki/British_Summer_Time
    [1] https://en.wikipedia.org/wiki/Central_European_Time
    [2] https://en.wikipedia.org/wiki/British_Summer_Time#Periods_of...

    • そのような形式に関するドラフト文書として IXDTF(Internet Extended Date/Time Format)があります[0]。RFC 3339 の文字列の後ろに角括弧内の tz 名としてタイムゾーンを付けられ、現地時刻を表現するには UTC オフセットの推定値も併記する必要があります
      たとえば 2030-07-01 18:00:00 Europe/London2030-07-01T18:00:00+01:00[Europe/London] になります。その前に英国の規則が変わるとタイムスタンプは「不一致」となり、アプリケーションが処理方法を決めます。ただし、角括弧内のタイムゾーン名の前に ! を入れると、UTC オフセットに盲目的に従わず、問題を検出しなければなりません
      この拡張タイムスタンプ形式は、JavaScript の提案中の Temporal ライブラリ[1]でも使われており、ZonedDateTime.from() のパース関数[2]では、不一致のタイムスタンプでどちらを優先するかを offset オプションで制御できます。UTC オフセットを省略してタイムゾーンだけを書くこともサポートしていますが、夏時間の切り替え時に繰り返される1時間の区間は曖昧だと警告しています
      [0] https://www.ietf.org/archive/id/draft-ietf-sedate-datetime-e...
      [1] https://tc39.es/proposal-temporal/docs/strings.html#iana-tim...
      [2] https://tc39.es/proposal-temporal/docs/ambiguity.html#ambigu...
    • 実際には、タイムゾーンよりさらに具体的な情報が必要に見えます。たとえばロンドンではなく、スコットランドのグラスゴーで2030年7月1日午後6時に会いたいとします。現在、グラスゴーは Europe/London タイムゾーンにあります
      しかしその間にスコットランドが独立住民投票を再び行い、中央ヨーロッパ時間に加わったり、Scottish Standard Time を作ったりする可能性も想像不可能ではありません
    • このような表現の落とし穴は、曖昧または不可能なタイムスタンプが生じることです。2023-11-05 01:30:00 America/New_York は、異なる2つの時刻のどちらかになります
      カレンダーでは「壁時計上の同じ時刻」がたいてい意図する意味なので妥当ですが、奇妙な時刻を処理する UI 上の難しさがあり、構文に曖昧性を解消する方法を入れたくなるかもしれません。通常このような切り替えは真夜中なので幸いですが、実務でこうした事例を見たことがあります
      夏時間のないタイムゾーンの人を招待すると、その人のカレンダー上では時刻が揺れ、海外の同僚がそれを受け入れなければならない場合があります
    • カレンダーの世界の iCal ではすでにサポートされています。タイムゾーンのない日付時刻は、現地時刻だけを意味します[0]
      [0]: https://www.rfc-editor.org/rfc/rfc5545#section-3.3.5
    • 必要な情報は、日付、場所、現地時刻の3つです。実際に欲しいのはタイムゾーンではないかもしれません。ロンドンではないある場所が別のタイムゾーンに移ったらどうするかを考える必要があります
      一般的な日付時刻形式は特定の時点を表すために作られていますが、この場合にはそのような特定の時点はまだ存在しません。毎月最終金曜日の会議、1月31日に始まる月例会議、四半期末の2日前といったように、日付/時刻指定が自明でない構造を持つことはよくあります
      人々が思いつくあらゆるケースを網羅する標準を作ろうとすると、すぐに複雑になりそうです。単純な日付、時刻、瞬間では足りないなら、必要な要素をすべて含む別の構造を作るしかなさそうです
  • ISOの仕様は無料で提供されていないため、通常はRFCに従うほうがよく、多くのオープンソース実装も草案ベースなので、完全にオープンソースフレンドリーとは言いにくい。オープンソース開発者にとっても大きな負担である
    将来の日付を扱う何かを作るなら、ほぼ常に壁時計時刻 + 場所を保存したくなる。残念ながら、これに対する標準はない。ヨーロッパや米国ではタイムゾーンがかなり安定しているので実感しにくいかもしれないが、多くの地域ではタイムゾーンが頻繁に変わるため、オフセットを保存するのは安定しない
    2026年6月5日 13:30 壁時計、パリが大多数の人の意図するところであり、EUが夏時間をどう扱うかによって UTC+2 かもしれないし UTC+1 かもしれない。過去の時刻を扱うAPIなら、単に秒、ミリ秒、マイクロ秒、ナノ秒単位のPOSIXタイムスタンプを使えばよい

    • iCalendarはこのためのRFC標準である[1]。ただし夏時間で時刻が変わる場合、ある時刻は2通りの表現を持ち、ある時刻はこの形式では表現できない
      iCalも、場所が別のタイムゾーンに再割り当てされる問題までは考慮していない。また、正しいiCalファイルは参照するすべてのタイムゾーンデータを含むため、単一の日時を1つ出力したいだけの場合には面倒である
      [1] https://icalendar.org/iCalendar-RFC-5545/3-3-5-date-time.htm...
    • RFC 3339を拡張し、角括弧内にIANAタイムゾーン名を入れるIXDTFという草案標準がある: 2026-06-05T13:30+0200[Europe/Paris]
      https://www.ietf.org/archive/id/draft-ietf-sedate-datetime-e...
  • 標準でしばしば無視される部分が期間の表現である
    http://xml.coverpages.org/ISO-FDIS-8601.pdfの5.5.4.2 “Representation of time-interval by duration only”、21ページを見ればよい。静的言語のJSONパーサがフィールドを期間として定義し、有効な形式でシリアライズできるようにしてくれるとよい
    Crystalの提案例はここにある: https://github.com/crystal-lang/crystal/issues/11942
    たとえば15日5時間20秒はP15DT5H0M20S、7週間はP7Wになる

    • 代わりにRFC 3339付録AのABNFにあるduration定義を参照してもよい
    • 構造化できるデータを、なぜ文字列形式にしておきたいのかわからない
      たとえば"duration": { "days": 15, "hours": 5, "seconds": 20 }のように書けば、JSONパーサがデータの意味を理解する必要はなく、入力バリデータに任せればよい。いずれにせよ、JSONがこのデータをどのように表現するにしても、失敗しうる変換ステップは必要である
  • RFC 3339とISO 8601は目的が重なる冗長な日時形式を多数含んでいる一方で、あらゆるシステムで最もよく使われ、あまりにも明白な2023-09-01 15:30:59はどちらにも含まれていないのが「おかしい」
    また、両標準とも紀元前の日付9999-12-31以降、または-9999-01-01以前の日付をどう表現するかが非常に不明確で、一般的なライブラリはたいていまったく扱えない。扱えたとしても、00-01-01の動作は実質的に未定義に近い
    グレゴリオ暦は紀元前1年の次の年が紀元1年なので奇妙で、専門的な天文ソフトウェアを除けば、ほとんどすべてのソフトウェアは近い将来のUnix時間の範囲外を正しく扱えない。アウグストゥス帝の生没年のように文字列として保存するだけで十分なものについても、標準で明確に定義してくれればよい

    • ISO 8601は相互合意があればその形式を許容する。「相互合意」というと大げさに聞こえるが、Tを空白に置き換えられるISO 8601、という単純な限定を付ければよいだけである。RFC 3339も、ずっと冗長なやり方で同様のことをしている
      2023-09-01 15:30:59が最もよく使われる日時形式なのかも断言しにくい。最も多く使われている言語は中国語であり、2023年9月1日のような固有の区切り文字が一般的である
      ISO 8601は相互合意があれば1582年以前や9999年以後の年も許容する。4桁に収まらない場合は、先頭に単一の符号文字を付ける必要がある。このような日付で意味のあることができる場面は少ないため一般にはサポートされないが、特にCとは独立に実装されたライブラリでは、パースする例をかなり見かける
      00-01-01紀元前1年1月1日として定義されている。ISO 8601は年番号が先発グレゴリオ暦(proleptic Gregorian calendar)に従うことを明確にしているため、負の無限大まで外挿される
    • 空白で区切った形式のほうがずっと読みやすい。ほぼ20年間標準を守ってきたが、最近はより良い空白区切り形式のほうを使うため、どちらも無視し始めている
      空白以外の文字が必要だった理由はわかるが、少なくともアンダースコアやピリオドを使うこともできたはずだ。そして文字列なのに年の範囲を4桁に制限し、形式の普遍性を犠牲にした理由もわからない
  • 6桁の年は、将来志向に見せるための、実際には起こらない問題への解決策のように感じる。現在の技術や社会規範が8000年も続くはずがない

    • コンピュータを今日に関係することだけに使うわけではない。たとえば非常に長い気候計算を走らせるとしたら、日付のせいでエラーになっても無視はできるかもしれないが、起きないほうがよいのではないか?
    • 5桁でも7桁でも、通信を始める前に双方が合意できる桁数なら使える。標準の第2部には10桁の年の例もある
    • 6桁の年は、今すでにある問題への解決策である。地質学者が大陸移動をシミュレーションすることを考えればよい
  • ISO 8601 が U+2010 HYPHEN と U+2212 MINUS を使い、該当する文字がない文字集合では U+2D HYPHEN-MINUS を使うべきだという説明は誤り
    実際の ISO 8601 は、対象の文字集合が ISO/IEC 646 ベースであれば、どちらの場合もハイフンマイナス文字を使うべきだと明記している。これには Unicode が確実に含まれる。多少の曖昧さはあるが、Unicode では解釈は明確で、646 の正規マッピングを指定して 646 ベースの他の文字集合との互換性を保証しようとする迂回的な方法に見える

    • 関連する段落は ISO 8601-1:2019 §3.2.1 にある
      「日付と時刻の表現に使われるすべての文字は、‘hyphen’, ‘minus’, ‘plus-minus’ を除けば ISO/IEC 646 のレパートリに属する。ISO/IEC 646 ベースの文字レパートリを使う環境では、‘hyphen’ と ‘minus’ はどちらも ‘hyphen-minus’ にマッピングしなければならない」という内容
      Unicode は ISO 8859 ベースで、ISO 8859 は ISO 646 ベースなので、Unicode 文字集合では U+2D hyphen-minus を使わせる意図で合っているように見える
  • Windows ではコロンが特殊文字なので、ファイル名に日付と時刻を入れるときに RFC 3339 に合う方法がない点がよく気になる
    日付にはハイフンを使いつつ、コロンは省略しても ISO 8601 準拠にできるとよい。たとえば 20230831T1510-0500 は準拠していてファイル名に使えるが、2023-08-31T1510-0500 や類似の変形はそうではない。付け加えると、PowerShell の Get-Date 関数は、ハイフンもコロンもない最初のタイムスタンプを理解できない

    • コロンを取り除いても安全で、RFC 3339 の日付や日時を曖昧にはしない。いつでも損失なくコロンを復元できる
    • Windows にこういう問題があるとは知らなかった。MacOS もファイル名のコロンに関して別の問題がある。最もよく使われる OS 3 つのうち 2 つにこの問題があるなら、設計で明らかに考慮が足りなかったということになる
  • このテーマについて非常によい可視化
    日付部分と時刻部分を区切るときは T の代わりに空白やアンダースコアを好むが、ISO 8601 だけを処理するものとの問題を避け、一貫性のためにたいてい T を使い続けている

    • T を好む。こういう文字列を偶然 T を基準に分割することはないだろうから
  • 2 つ気になる。第一に、6 桁の年の根拠は何なのか?今考案したどんなシステムも 100,000 年後に存在するはずがないのでは
    第二に、ISO 8601 は広く普及しているが、RFC 3339 も実際のシステムで多く使われ、採用されているのか?

    • 古い技術から完全に抜け出すのにかかる時間を見ると、100,000 年後にも量子コンピュータ上で x86 をエミュレーションしていても驚かない気がする
    • ライブラリが ISO 8601 と言うとき、90% は実際には ISO 8601 のより難解な部分まで実装していない
    • Golang の公式 time パッケージと Rust の Chrono には RFC 3339 を扱う組み込みツールがあるが、RFC 8601 用はない
      記憶では RFC 8601 には RFC 3339 にはない曖昧性の問題があり、Python もこのために日付の往復変換で問題があったと認識している
    • 科学用ツールなら、遠い未来の日付を表現したいことがあるかもしれない
    • Y10k (:
  • タイムゾーンがコロンなしの 4 桁で指定される形式はないのに、date +%z±NNNN を返す

    • 幸い %:z がある。関連して、date がデフォルトでこのように出力するよう教えることもできる: https://gist.github.com/d081dad407432d53172e30d0d35c39db
      $ date
      2023-08-31T11:15:00-07:00
    • ±NNNN は「基本形式」の一部として使われるなら、コロンなしでも有効。つまり形式全体のどこにもハイフンやコロンがあってはならない
      したがって次の 2 つは同等で、どちらも有効:
      2023-09-01T09:40:01+08:00
      20230901T094001+0800