[インデックス 13164] ファイルの概要
このコミットは、Go言語の標準ライブラリである net/mail
パッケージにおける日付ヘッダーのパース処理を改善するものです。具体的には、RFC 5322で厳密には定義されていないものの、広く使用されている日付フォーマット(例: タイムゾーン情報に括弧で囲まれた略称が含まれる形式)を net/mail
が正しく解釈できるように、パースロジックをより柔軟にしています。これにより、様々なメールクライアントやシステムから送信されるメールの Date
ヘッダーを、より堅牢に処理できるようになります。
コミット
commit 132dbb61aadf2eaa0718e388b2bb7e3f6e42f734
Author: Bill Thiede <couchmoney@gmail.com>
Date: Fri May 25 09:19:21 2012 +1000
net/mail: more liberal parsing of Date headers.
Fixes #3639.
R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/6243045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/132dbb61aadf2eaa0718e388b2bb7e3f6e42f734
元コミット内容
net/mail
パッケージにおいて、Date
ヘッダーのパース処理をより寛容にする変更です。Go issue #3639 を修正します。
変更の背景
メールの Date
ヘッダーは、RFC 5322 (Internet Message Format) によってそのフォーマットが規定されています。しかし、現実の世界では、様々なメールクライアントやシステムがこのRFCの規定に厳密に従わない、あるいはRFCで明示的に定義されていないが一般的に使用されている形式で Date
ヘッダーを生成することがあります。
このコミットは、特にタイムゾーンの表記に関して、RFC 5322では定義されていない "-0700 (MST)"
のような形式(タイムゾーンのオフセットの後に括弧で囲まれたタイムゾーンの略称が続く形式)が原因で、net/mail
パッケージが Date
ヘッダーを正しくパースできない問題(Go issue #3639)を解決するために導入されました。
net/mail
パッケージは、メールのヘッダーを解析し、構造化されたデータとして提供する役割を担っています。Date
ヘッダーのパースに失敗すると、メールの送信日時を正確に取得できず、アプリケーションの動作に影響を与える可能性があります。この変更は、net/mail
パッケージの堅牢性を高め、より多くの種類のメールを適切に処理できるようにすることを目的としています。
前提知識の解説
RFC 5322 (Internet Message Format)
RFC 5322は、インターネットメッセージ(主に電子メール)の標準フォーマットを定義する仕様です。このRFCは、メッセージのヘッダーフィールド(From
, To
, Subject
, Date
など)の構文と意味、およびメッセージ本文の構造を詳細に規定しています。
特に Date
ヘッダーに関しては、以下のようなフォーマットが推奨されています。
date-time = [ day-of-week "," ] day month year hour ":" minute [ ":" second ] FWS zone
例: Fri, 21 Nov 1997 09:55:06 -0600
ここで zone
は、UTCからのオフセット(例: -0600
)または特定のタイムゾーン略称(例: GMT
, EST
)で表されます。RFC 5322は、タイムゾーンの略称を括弧で囲む形式については明示的に言及していませんが、一部のシステムでは慣習的に使用されていました。
Go言語の time
パッケージと日付フォーマット
Go言語の time
パッケージは、日付と時刻を扱うための強力な機能を提供します。特に、time.Parse
関数は、指定されたレイアウト文字列に基づいて文字列から time.Time
オブジェクトをパースするために使用されます。
Go言語の time
パッケージにおけるレイアウト文字列は、特殊な参照時刻 Mon Jan 2 15:04:05 MST 2006
を使用して定義されます。この参照時刻の各要素が、パースまたはフォーマットしたい日付/時刻文字列の対応する要素にマッピングされます。
Mon
: 曜日 (例:Mon
,Tue
)Jan
: 月 (例:Jan
,Feb
)2
: 日 (例:1
,02
)15
: 時 (24時間形式)04
: 分05
: 秒MST
: タイムゾーン略称 (例:MST
,GMT
)2006
: 年 (4桁)-0700
: タイムゾーンオフセット (例:-0700
,+0800
)
net/mail
パッケージは、Date
ヘッダーをパースするために、RFC 5322で定義されている様々な日付フォーマットに対応する複数のレイアウト文字列を内部的に保持しています。これらのレイアウト文字列を順に試行し、最初にマッチしたものでパースを試みます。
技術的詳細
このコミットの技術的な核心は、src/pkg/net/mail/message.go
ファイル内の dateLayouts
スライスを生成するロジックに、新しいタイムゾーンのフォーマットを追加した点です。
元のコードでは、タイムゾーンの表現として "-0700"
(UTCからのオフセット) と "MST"
(タイムゾーン略称) の2種類を考慮していました。しかし、Go issue #3639 で報告された問題は、"-0600 (MDT)"
のように、オフセットの後に括弧で囲まれたタイムゾーン略称が続く形式がパースできないことでした。
このコミットでは、zones
配列に "-0700 (MST)"
という新しい要素を追加しています。これにより、dateLayouts
が生成される際に、この新しいタイムゾーン形式を含む日付レイアウト文字列も含まれるようになります。
具体的には、init()
関数内で dateLayouts
を生成する際に、dows
, days
, months
, years
, hours
, minutes
, seconds
, zones
の各要素を組み合わせて可能な日付レイマットを網羅的に生成しています。zones
に "-0700 (MST)"
が追加されたことで、net/mail
はこの形式の Date
ヘッダーも認識し、正しくパースできるようになります。
また、src/pkg/net/mail/message_test.go
には、この新しいフォーマットをテストするための新しいテストケースが追加されています。これは、"Fri, 21 Nov 1997 09:55:06 -0600 (MDT)"
という文字列が、期待される time.Time
オブジェクトに正しくパースされることを検証します。このテストケースの追加は、変更が意図通りに機能し、将来のリグレッションを防ぐ上で非常に重要です。
コアとなるコードの変更箇所
diff --git a/src/pkg/net/mail/message.go b/src/pkg/net/mail/message.go
index 0917bbedf1..b610ccf3f0 100644
--- a/src/pkg/net/mail/message.go
+++ b/src/pkg/net/mail/message.go
@@ -69,11 +69,12 @@ var dateLayouts []string
func init() {
// Generate layouts based on RFC 5322, section 3.3.
-\tdows := [...]string{\"\", \"Mon, \"} // day-of-week
-\tdays := [...]string{\"2\", \"02\"} // day = 1*2DIGIT
-\tyears := [...]string{\"2006\", \"06\"} // year = 4*DIGIT / 2*DIGIT
-\tseconds := [...]string{\":05\", \"\"} // second
-\tzones := [...]string{\"-0700\", \"MST\"} // zone = ((\"+\" / \"-\") 4DIGIT) / \"GMT\" / ...
+\tdows := [...]string{\"\", \"Mon, \"} // day-of-week
+\tdays := [...]string{\"2\", \"02\"} // day = 1*2DIGIT
+\tyears := [...]string{\"2006\", \"06\"} // year = 4*DIGIT / 2*DIGIT
+\tseconds := [...]string{\":05\", \"\"} // second
+\t// \"-0700 (MST)\" is not in RFC 5322, but is common.
+\tzones := [...]string{\"-0700\", \"MST\", \"-0700 (MST)\"} // zone = ((\"+\" / \"-\") 4DIGIT) / \"GMT\" / ...
for _, dow := range dows {
\tfor _, day := range days {
diff --git a/src/pkg/net/mail/message_test.go b/src/pkg/net/mail/message_test.go
index 671ff2efac..fd17eb414a 100644
--- a/src/pkg/net/mail/message_test.go
+++ b/src/pkg/net/mail/message_test.go
@@ -95,6 +95,11 @@ func TestDateParsing(t *testing.T) {\n \t\t\t\"21 Nov 97 09:55:06 GMT\",\n \t\t\ttime.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone(\"GMT\", 0)),\n \t\t},\n+\t\t// Commonly found format not specified by RFC 5322.\n+\t\t{\n+\t\t\t\"Fri, 21 Nov 1997 09:55:06 -0600 (MDT)\",\n+\t\t\ttime.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone(\"\", -6*60*60)),\n+\t\t},\n \t}\n \tfor _, test := range tests {\n \t\thdr := Header{\n```
## コアとなるコードの解説
### `src/pkg/net/mail/message.go` の変更
`zones` 配列の定義が変更されました。
```go
- zones := [...]string{"-0700", "MST"} // zone = (("+" / "-") 4DIGIT) / "GMT" / ...
+ // "-0700 (MST)" is not in RFC 5322, but is common.
+ zones := [...]string{"-0700", "MST", "-0700 (MST)"} // zone = (("+" / "-") 4DIGIT) / "GMT" / ...
この変更により、zones
配列に "-0700 (MST)"
という新しい文字列が追加されました。これは、タイムゾーンのオフセットの後に括弧で囲まれたタイムゾーン略称が続く形式を表しています。init()
関数内で dateLayouts
が生成される際、この新しい zones
の要素が他の日付/時刻要素と組み合わされ、net/mail
がパースを試みる日付レイアウトの候補に追加されます。これにより、RFC 5322では厳密に定義されていないが、実世界で広く使用されている日付フォーマットに対応できるようになります。コメントで「RFC 5322にはないが一般的」と明記されている点が、この変更の意図を明確に示しています。
src/pkg/net/mail/message_test.go
の変更
TestDateParsing
関数に新しいテストケースが追加されました。
+ // Commonly found format not specified by RFC 5322.
+ {
+ "Fri, 21 Nov 1997 09:55:06 -0600 (MDT)",
+ time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
+ },
このテストケースは、"Fri, 21 Nov 1997 09:55:06 -0600 (MDT)"
という文字列が、net/mail
パッケージによって正しくパースされることを検証します。期待される結果は、time.Date
関数で生成された time.Time
オブジェクトであり、タイムゾーンは -0600
(MDT) に対応する固定ゾーンとして設定されています。このテストの追加は、message.go
で行われた変更が期待通りに機能することを確認し、将来のコード変更によってこの機能が損なわれないようにするための重要なステップです。コメントで「RFC 5322で指定されていないが、一般的に見られるフォーマット」と説明されており、このテストケースがカバーする具体的なシナリオを明確にしています。
関連リンク
- RFC 5322 - Internet Message Format: https://datatracker.ietf.org/doc/html/rfc5322
- Go issue #3639: https://github.com/golang/go/issues/3639
参考にした情報源リンク
- Go issue #3639 の内容 (コミットメッセージから参照)
- RFC 5322 の日付フォーマットに関する一般的な知識
- Go言語の
time
パッケージのドキュメントに関する一般的な知識 - コミットの差分情報