TOML の 4 種類の時刻型:Offset Date-Time / Local Date-Time / Local Date / Local Time の使い分け
TOML の時刻型は 4 種類あり、タイムゾーンの有無と「日付・時刻」の組み合わせで使い分けます。「JSON は文字列で何でも書けるが、TOML は型として日付を持つ」という違いに気づきにくく、実装で踏みやすい点があります。本記事では 4 種類を比較し、Cargo や pyproject.toml での使い方を整理します。
4 種類の時刻型
TOML v1.0.0 仕様 の §「日時」で定義されています:
| 型 | 例 | TZ 情報 | 用途 |
|---|---|---|---|
| Offset Date-Time | 1979-05-27T07:32:00-07:00 | あり | 絶対時刻(イベント発生時刻、ログのタイムスタンプ) |
| Local Date-Time | 1979-05-27T07:32:00 | なし | 「現地時刻」(カレンダーイベントなど) |
| Local Date | 1979-05-27 | なし | 日付のみ(誕生日、リリース日) |
| Local Time | 07:32:00 | なし | 時刻のみ(毎日の業務開始時刻など) |
Offset Date-Time
RFC 3339 形式で、タイムゾーンオフセットまで含む完全な時刻:
created = 1979-05-27T07:32:00-07:00
updated = 1979-05-27T15:32:00Z # Z = UTC Zは+00:00の省略形(UTC)- ミリ秒・マイクロ秒も使える:
1979-05-27T00:32:00.999999Z - 「いつ起きたかが世界共通で確定する」セマンティクス
イベントログ・トランザクション時刻・API レスポンスなど、世界の絶対時刻が要る場面で使う型。
Local Date-Time
タイムゾーン抜きの日時:
event_starts = 1979-05-27T07:32:00 - 「現地時刻として 5 月 27 日 7 時 32 分」を意味するが、どのタイムゾーンの 7:32 かは TOML だけでは決まらない
- カレンダーイベント・リマインダーなど、観察者の現地時刻が重要な場合に使う
「3 月 11 日 14 時 46 分の地震発生時刻」を Offset Date-Time で書くと UTC + 9:00 などのオフセット込みで世界の絶対時刻が決まります。一方、「会議は東京時間で 9 時、ニューヨーク時間で 9 時、両方をローカルでやる」という運用なら Local Date-Time のほうが自然。
Local Date
日付のみ:
release_date = 1979-05-27 時刻情報なし。誕生日・締切日・リリース日など、「その日の特定時刻」ではなく日付そのものが情報の場合。
Local Time
時刻のみ:
opening_hours = 09:00:00 特定の日付に紐付かない時刻。「毎日 9 時開始」のような繰り返し情報。
YAML / JSON との対応
JSON
JSON には時刻型がないので、ISO 8601 文字列で表現するのが慣習:
{ "created": "1979-05-27T07:32:00-07:00" } パーサが文字列として返すので、利用側で Date.parse などしてオブジェクト化する必要があります。
YAML
YAML は時刻型を持ちます(!!timestamp タグ):
created: 1979-05-27T07:32:00-07:00 ただし Norway Problem のように暗黙の型推論が罠を引き起こすため、strictyaml のように暗黙型を全部撤廃する派生もあります。
TOML はどっちつかず?
TOML は型を明示的に持つが、その分「Local」と「Offset」を書き分ける必要があります。これは使う側にどれが必要かを意識させる設計判断で、JSON の「全部文字列」より厳格、YAML の「暗黙推論」より明示的。
実装例
Cargo (Cargo.toml)
Rust プロジェクトの設定ファイル。バージョン番号は文字列、ライセンスも文字列、日付型はあまり登場しません。代わりにバージョン要件に Local-Date 風の表記を使う場面が:
[package]
name = "my-crate"
version = "0.1.0"
rust-version = "1.65"
publish-date = 2024-03-15 # Local Date publish-date は仕様にはなく、ユーザーが拡張で書くケース。
pyproject.toml
Python プロジェクト設定:
[project]
name = "my-package"
version = "0.1.0"
release-date = 2024-03-15
[tool.bumpversion]
current_version = "0.1.0"
last_bumped = 2024-03-15T10:00:00+09:00 ビルド時刻を残したい場合に Offset Date-Time が役立ちます。pyproject.toml の主要フィールドは仕様で文字列・配列・テーブルしか要求していないので、時刻型は [tool.*] の独自セクションで使われることが多い。
Hugo / Zola の front matter
静的サイトジェネレータの記事メタデータでは TOML が選ばれることがあります:
+++
title = "記事タイトル"
date = 2024-03-15T09:00:00+09:00
expiry_date = 2024-12-31
+++ date は Offset Date-Time(記事公開の世界絶対時刻)、expiry_date は Local Date(その日まで有効)と使い分けるのが正攻法。
パースして言語ネイティブの型へ
各言語のパーサが返す型:
| 言語 | TOML パーサ | Offset Date-Time | Local Date-Time | Local Date | Local Time |
|---|---|---|---|---|---|
| Python 3.11+ | tomllib(標準) | datetime (tz-aware) | datetime (naive) | date | time |
| Rust | toml crate | OffsetDateTime (時刻 crate 利用時) または文字列 | PrimitiveDateTime | Date | Time |
| Go | BurntSushi/toml | time.Time (zone あり) | time.Time (zone なし) | カスタム型 | カスタム型 |
| JavaScript | @iarna/toml | Date | Date (UTC として解釈) | Date | カスタム |
JavaScript のように Date 型が時刻のみ・日付のみを表現できない言語では、すべて Date オブジェクトに丸められて情報が落ちる場合があります。Local Time を Date に変換すると 1970-01-01T<時刻> のように扱われがち。
落とし穴
1. Offset Date-Time に Z を付け忘れて Local Date-Time として解釈される
event = 1979-05-27T07:32:00 # Local Date-Time(UTC ではない!)
event = 1979-05-27T07:32:00Z # Offset Date-Time Z も +00:00 も付けないと Local Date-Time として解釈されます。Z を忘れて意図せずローカル時刻になってしまう、というのが頻発する誤り。
2. ミリ秒の桁数
event = 1979-05-27T07:32:00.123 # ミリ秒
event = 1979-05-27T07:32:00.123456 # マイクロ秒
event = 1979-05-27T07:32:00.123456789 # ナノ秒(実装依存) 仕様上は 任意の桁数を許容しますが、パーサによってはマイクロ秒・ナノ秒以上を切り捨てます。タイムスタンプの精度を保ちたいなら、桁数の上限をパーサで確認。
3. Local Date-Time を保存して、その後タイムゾーン解釈で困る
「データベースには Local Date-Time として書いて、利用側でタイムゾーンを後付けする」設計は柔軟に見えるが、タイムゾーン情報が後から来ない場合は致命的に解釈不能。Offset Date-Time で書ける情報があるならそうすべき。
用途別の早見表
- イベント発生時刻、ログ、API レスポンス → Offset Date-Time(タイムゾーン固定)
- カレンダーイベント、現地時刻が意味を持つもの → Local Date-Time
- 誕生日、リリース日、契約開始日 → Local Date
- 毎日の開始時刻、繰り返し時刻 → Local Time
- TOML 以外の API と相互運用するなら ISO 8601 文字列 → 仕様外だが普及
まとめ
TOML の 4 つの時刻型は、JSON の「全部文字列」と YAML の「暗黙推論」の中間で、書き手に明示を要求する設計です。Offset Date-Time と Local Date-Time の混同は最も頻発する誤りで、Z の有無だけで意味が変わる点を把握しておけば、ほとんどの罠は避けられます。
TOML を JSON に変換して構造を確認したいときは TOML to JSON 変換ツール が利用できます。