メインコンテンツまでスキップ

4. Local Time (ローカル時刻)

4.1. Coordinated Universal Time (UTC) (協定世界時)

ローカルタイムゾーンの夏時間ルールは非常に複雑で、予測不可能な時期に現地の法律に基づいて変更される可能性があるため、真の相互運用性は協定世界時 (Coordinated Universal Time, UTC) を使用することで最もよく達成されます。本仕様は、ローカルタイムゾーンルールには対応していません。

UTCを使用する理由:

  • ✅ 世界統一の時間基準
  • ✅ 夏時間の影響を受けない
  • ✅ 政治的決定の影響を受けない
  • ✅ 相互運用性を保証

4.2. Local Offsets (ローカルオフセット)

ローカル時刻とUTCの間のオフセットは、多くの場合有用な情報です。例えば、電子メール (RFC2822, [IMAIL-UPDATE]) では、ローカルオフセットは迅速な応答の可能性を判断するための有用なヒューリスティックを提供します。過去にアルファベット文字列でローカルオフセットをラベル付けしようとした試みは、相互運用性の低下をもたらしました [IMAIL], [HOST-REQ]。その結果、RFC2822 [IMAIL-UPDATE] は数値オフセットを必須としました。

数値オフセットの計算

数値オフセットは 「ローカル時刻マイナスUTC」 として計算されます。したがって、ローカル時刻からオフセットを減算することで、UTCでの等価時刻を決定できます。

例1:

ローカル時刻: 18:50:00-04:00
UTC時刻: 18:50:00 - (-04:00) = 18:50:00 + 04:00 = 22:50:00Z

検証: 東部夏時間 (EDT) はUTC-4

例2:

ローカル時刻: 15:30:00+08:00
UTC時刻: 15:30:00 - (+08:00) = 15:30:00 - 08:00 = 07:30:00Z

検証: 中国標準時 (CST) はUTC+8

重要な注意事項

注意: ISO 8601に従い、数値オフセットはUTCから整数分だけ異なるタイムゾーンのみを表します。ただし、多くの歴史的なタイムゾーンはUTCから非整数分異なります。このような歴史的タイムスタンプを正確に表現するには、アプリケーションはそれらを表現可能なタイムゾーンに変換しなければなりません (MUST)。

歴史的タイムゾーンの例:

19世紀後半の一部のローカル時刻:
- アムステルダム: UTC+00:19:32
- パリ: UTC+00:09:21

これらはRFC 3339で正確に表現できず、最も近い分に丸める必要があります

4.3. Unknown Local Offset Convention (未知のローカルオフセット規則)

UTCでの時刻は既知であるが、ローカル時刻へのオフセットが未知である場合、これはオフセット "-00:00" で表すことができます。これは、"Z" または "+00:00" のオフセットとは意味的に異なります。後者は、UTCが指定された時刻の優先参照点であることを意味します。RFC2822 [IMAIL-UPDATE] は、電子メールに対して同様の規則を説明しています。

3つの表現の区別

Z または +00:00:

2002-07-15T10:30:00Z
2002-07-15T10:30:00+00:00

意味: この時刻はUTC時刻であり、UTCが優先参照点です

-00:00:

2002-07-15T10:30:00-00:00

意味: UTC時刻は10:30:00ですが、ローカルタイムゾーンオフセットは未知です
(タイムゾーン設定を知らないシステムからの可能性があります)

使用シナリオ:

シナリオ1: サーバーログ、UTCであることが既知 → Zを使用
シナリオ2: デバイス生成タイムスタンプ、デバイスはUTCだがローカルゾーンを知らない → -00:00を使用
シナリオ3: 明示的にロンドン (GMT) → +00:00を使用

4.4. Unqualified Local Time (非限定ローカル時刻)

現在インターネットに接続されている多数のデバイスは、内部クロックをローカル時刻で実行しており、UTCを認識していません。インターネットは仕様を設計する際に現実を受け入れる伝統を持っていますが、これは相互運用性を犠牲にして行われるべきではありません。非限定ローカルタイムゾーンの解釈は、地球上の約23/24で失敗するため、

必須要件

インターネットプロトコルは完全修飾されたタイムスタンプを生成しなければなりません (MUST)。

これは、インターネットプロトコルがタイムゾーン情報なしでローカル時刻を使用してはならない (MUST NOT) ことを意味します。

誤った例:

❌ 2002-07-15T10:30:00 (タイムゾーン情報なし)

正しい例:

✅ 2002-07-15T10:30:00Z (UTC)
✅ 2002-07-15T10:30:00+08:00 (明示的なタイムゾーン)
✅ 2002-07-15T10:30:00-00:00 (UTCだがゾーンは未知)

相互運用性の問題

非限定ローカル時刻が使用された場合:

送信者: 2002-07-15T10:30:00 (ニューヨークのローカル時刻、実際はUTC-4)
東京の受信者: 東京時間 (UTC+9) と誤解釈
時刻差エラー: 13時間!

実装の推奨事項

システム設計

# 推奨: 常にUTCで時刻を保存
def store_timestamp():
utc_time = datetime.now(timezone.utc)
return utc_time.isoformat() # 2024-12-21T10:30:00+00:00

# 表示時: ユーザーのローカルタイムゾーンに変換
def display_timestamp(utc_time, user_timezone):
local_time = utc_time.astimezone(user_timezone)
return local_time.isoformat()

データベースストレージ

-- 推奨: TIMESTAMP WITH TIME ZONEを使用
CREATE TABLE events (
id SERIAL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- 避ける: TIMESTAMP WITHOUT TIME ZONE (曖昧さを引き起こす)

重要な原則: 内部的にはUTCで保存し、表示時にローカルタイムゾーンに変換します。データ交換にタイムゾーン情報のないタイムスタンプを決して使用しないでください。