Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 18934] ファイルの概要

このコミットは、Go言語の time パッケージにおいて、Time.MarshalJSON メソッドが年を [0000, 9999] の範囲外で拒否する理由を説明するコメントを追加するものです。これは、RFC 3339 の仕様に準拠するためであり、過去にこの挙動に関する質問が複数回発生したため、将来の疑問を解決するために行われました。

コミット

time: add comment explaining rejection of years outside [0000,9999]

This has come up twice now. Redirect future questions
to the explanation in the issue tracker.

LGTM=iant, r
R=r, iant
CC=golang-codereviews
https://golang.org/cl/79550043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/3b27343c14fdfeaa19b20b26ce660aafa814d01d

元コミット内容

time: add comment explaining rejection of years outside [0000,9999]

このコミットは、time パッケージの Time.MarshalJSON 関数が、年が0から9999の範囲外である場合にエラーを返す理由について、コード内に説明コメントを追加するものです。この挙動はRFC 3339の規定によるものであり、過去に同様の質問が複数回寄せられたため、将来の混乱を避ける目的で、関連するIssueトラッカーの議論への参照とともにコメントが追加されました。

変更の背景

この変更の背景には、Go言語の time パッケージにおける Time.MarshalJSON メソッドの挙動に関するユーザーからの頻繁な問い合わせがありました。Time.MarshalJSON は、time.Time 型の値をJSON形式にマーシャリングする際に、RFC 3339 (Date and Time on the Internet: Timestamps) に準拠した形式で出力します。

RFC 3339では、年の表現について厳格なルールがあり、特に「年は正確に4桁で表現されるべきである」と規定されています。このため、Goの time パッケージでは、年が0未満または9999を超える(つまり、4桁で表現できない)場合に MarshalJSON がエラーを返すように実装されていました。

しかし、この挙動がRFC 3339の仕様に基づくものであることが十分に理解されておらず、ユーザーから「なぜ特定の年の範囲外でエラーになるのか」という質問が繰り返し寄せられていました。このコミットは、このような繰り返しの質問に対応し、コード自体にその理由と関連する議論(golang.org/issue/4556#c15)への参照を追加することで、開発者やユーザーが将来的に同じ疑問を抱いた際に、より迅速に自己解決できるようにすることを目的としています。

前提知識の解説

RFC 3339 (Date and Time on the Internet: Timestamps)

RFC 3339は、インターネット上での日付と時刻の表現方法を定義する標準規格です。これはISO 8601のプロファイルを基にしており、特にタイムスタンプの交換に適しています。JSONなどのデータ形式で日付と時刻を表現する際によく利用されます。

RFC 3339の重要な特徴は以下の通りです。

  • 完全な日付と時刻の表現: 年、月、日、時、分、秒、およびタイムゾーンオフセットを含む完全な形式をサポートします。
  • 正確な桁数: 特に年については、正確に4桁で表現することを要求しています。例えば、西暦1年を 0001 と表現し、西暦10000年を 10000 と表現することはRFC 3339の規定外となります(4桁を超えるため)。
  • タイムゾーンの指定: UTCオフセット(例: +09:00Z (UTC))を明示的に含める必要があります。

Go言語の time パッケージは、このRFC 3339に準拠した日付/時刻のフォーマットとパースをサポートしており、time.RFC3339time.RFC3339Nano といった定数を提供しています。

Go言語の time パッケージ

Go言語の標準ライブラリに含まれる time パッケージは、日付と時刻を扱うための機能を提供します。主要な型は time.Time であり、特定の時点を表します。このパッケージは、時刻の取得、フォーマット、パース、期間の計算など、幅広い機能を提供します。

  • time.Time: 特定の瞬間を表す構造体です。
  • MarshalJSON メソッド: json.Marshaler インターフェースの一部として、time.Time 型の値をJSON形式に変換する際に呼び出されるメソッドです。このメソッドは、RFC 3339形式の文字列を生成します。

golang.org/issue/4556

これはGo言語の公式Issueトラッカーにおける特定のIssue(問題報告や議論)です。このIssueでは、time.MarshalJSON が年の範囲外でエラーを返す挙動について議論が行われました。特に、コメント15 (#c15) は、この挙動がRFC 3339の「年は4桁であるべき」という規定に基づいていることを明確に説明しています。このコミットは、このIssueの議論内容をコードに反映させることで、同様の疑問を持つ開発者へのガイダンスを提供しています。

技術的詳細

このコミットは、Go言語の src/pkg/time/time.go ファイル内の Time.MarshalJSON メソッドにコメントを追加するものです。このメソッドは、time.Time 型の値をJSON形式のバイトスライスに変換する役割を担っています。

既存のコードでは、t.Year() メソッドで取得した年 y が0未満または10000以上の場合、つまり年が4桁の範囲 [0000, 9999] を超える場合に、errors.New("Time.MarshalJSON: year outside of range [0,9999]") というエラーを返していました。

このコミットによって追加されたコメントは、このエラー挙動の根拠を明確にしています。具体的には、以下の2行が追加されました。

// RFC 3339 is clear that years are 4 digits exactly.
// See golang.org/issue/4556#c15 for more discussion.

このコメントは、Time.MarshalJSON が年の範囲を制限する理由が、RFC 3339が「年は正確に4桁である」と明確に規定しているためであることを示しています。また、golang.org/issue/4556#c15 への参照は、この設計判断に関する詳細な議論がIssueトラッカーで確認できることを示唆しており、より深い理解を求める開発者への道筋を提供します。

この変更自体はコードの動作を変更するものではなく、既存の挙動に対する説明を追加するドキュメンテーションの改善です。しかし、これにより、Goの time パッケージの設計思想とRFC 3339への準拠がより透過的になり、開発者が予期せぬエラーに遭遇した際のデバッグや理解が容易になります。

コアとなるコードの変更箇所

変更は src/pkg/time/time.go ファイルの Time.MarshalJSON メソッド内で行われました。

--- a/src/pkg/time/time.go
+++ b/src/pkg/time/time.go
@@ -934,6 +934,8 @@ func (t *Time) GobDecode(data []byte) error {
 // The time is a quoted string in RFC 3339 format, with sub-second precision added if present.
 func (t Time) MarshalJSON() ([]byte, error) {
  if y := t.Year(); y < 0 || y >= 10000 {
+\t\t// RFC 3339 is clear that years are 4 digits exactly.
+\t\t// See golang.org/issue/4556#c15 for more discussion.
  return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]")
  }
  return []byte(t.Format("`" + RFC3339Nano + "`")), nil

具体的には、if y := t.Year(); y < 0 || y >= 10000 { ... } という条件文の直前に2行のコメントが追加されました。

コアとなるコードの解説

追加されたコメントは、Time.MarshalJSON メソッドが年を検証し、特定の範囲外の年に対してエラーを返す理由を説明しています。

if y := t.Year(); y < 0 || y >= 10000 { この行は、time.Time オブジェクト t から年を取得し、その年 y が0未満であるか、または10000以上であるかをチェックしています。この条件が真の場合、つまり年が4桁の範囲 [0000, 9999] を超える場合に、続くエラー処理ブロックが実行されます。

// RFC 3339 is clear that years are 4 digits exactly. このコメントは、なぜ年がこの範囲に制限されるのかの直接的な理由を述べています。RFC 3339という標準規格が、年の表現を「正確に4桁」と規定しているためです。これにより、0000 から 9999 までの年のみが有効と見なされます。

// See golang.org/issue/4556#c15 for more discussion. このコメントは、この設計決定に関する詳細な背景や議論が、GoのIssueトラッカーの特定のコメント(Issue 4556のコメント15)で確認できることを示しています。これは、コードの背後にある設計思想を理解するための重要なリソースへのポインタとなります。

return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]") 年が指定された範囲外であった場合に、この行が実行され、エラーが返されます。これにより、RFC 3339の要件を満たさない日付/時刻データがJSONとして不正にマーシャリングされることを防ぎます。

この変更は、コードの機能的な動作には影響を与えませんが、その意図と根拠を明確にすることで、コードの可読性と保守性を向上させています。

関連リンク

参考にした情報源リンク

  • Stack Overflow: https://stackoverflow.com/questions/tagged/go+rfc3339 (RFC 3339とGoに関する一般的な議論)
  • Medium記事 (Goのtimeパッケージに関する記事): https://medium.com/ (具体的な記事は特定できませんでしたが、Goのtimeパッケージに関する解説記事が多数存在します)
  • Practical Go Lessons: https://practical-go-lessons.com/ (GoのtimeパッケージやJSONマーシャリングに関するレッスンがある可能性があります)
  • Studygolang.com: https://studygolang.com/ (Go言語の学習リソース、関連する議論がある可能性があります)
  • Google Web Search (for golang.org/issue/4556#c15): 実行したWeb検索の結果