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

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

このコミットは、Go言語の標準ライブラリにおけるtimeパッケージ内のsrc/pkg/time/format.goファイルに対する変更です。このファイルは、time.Time型の値と文字列との間で日付と時刻のフォーマットおよびパースを行うためのロジックを定義しています。特に、time.Parse()関数の挙動に関するドキュメントの改善が目的です。

コミット

このコミットは、time.Parse()関数が、指定されたレイアウト文字列に年情報が含まれていない場合に、デフォルトで「年0」を使用することについて、その挙動がGo言語のtime.Time型の「ゼロ値」(time.Time{})よりも前の時刻を生成する可能性があるという重要な注意書きを追加しています。これにより、ユーザーがtime.Parse()の戻り値の挙動を誤解するのを防ぎ、より正確な理解を促します。

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

https://github.com/golang/go/commit/e985d5464b45a116825603b0f72d07bbc7ae5ec5

元コミット内容

commit e985d5464b45a116825603b0f72d07bbc7ae5ec5
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Sat Jan 19 04:57:31 2013 +0800

    time: add note about Parse()'s choice of default year
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/7101046
---
 src/pkg/time/format.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/pkg/time/format.go b/src/pkg/time/format.go
index 417e8f8d7a..35118a9ff7 100644
--- a/src/pkg/time/format.go
+++ b/src/pkg/time/format.go
@@ -637,7 +637,8 @@ func skip(value, prefix string) (string, error) {
 //
 // Elements omitted from the value are assumed to be zero or, when
 // zero is impossible, one, so parsing "3:04pm" returns the time
-// corresponding to Jan 1, year 0, 15:04:00 UTC.
+// corresponding to Jan 1, year 0, 15:04:00 UTC (note that because the year is
+// 0, this time is before the zero Time).
 // Years must be in the range 0000..9999. The day of the week is checked
 // for syntax but it is otherwise ignored.
 func Parse(layout, value string) (Time, error) {

変更の背景

Go言語のtime.Parse()関数は、与えられたレイアウト文字列に基づいて時刻文字列をtime.Time型に変換します。この際、レイアウト文字列に年情報が含まれていない場合、time.Parse()はデフォルトで「年0」を使用します。

しかし、Go言語のtime.Time型には「ゼロ値」という概念があります。これはtime.Time{}で表現され、通常は「1年1月1日 00:00:00 UTC」に対応します。多くのプログラマーは、年0の時刻がこのゼロ値と同じか、あるいはそれ以降であると誤解する可能性があります。

実際には、年0の時刻は、time.Timeのゼロ値(1年1月1日)よりも前の時刻です。このコミットは、この重要なニュアンスをtime.Parse()のドキュメンテーションに追加することで、ユーザーが予期せぬ挙動に遭遇するのを防ぎ、より正確な理解を促すことを目的としています。特に、年0の時刻がtime.Timeのゼロ値よりも「前」であるという点を明確にすることで、時刻の比較や初期値の扱いで混乱が生じるのを防ぎます。

前提知識の解説

Go言語のtimeパッケージとtime.Time

Go言語の標準ライブラリには、日付と時刻を扱うためのtimeパッケージが用意されています。このパッケージの中心となるのがtime.Time型で、特定の時点を表します。time.Time型の値は、年、月、日、時、分、秒、ナノ秒、およびタイムゾーン情報を含みます。

time.Parse()関数

time.Parse(layout, value string) (Time, error)関数は、指定されたlayout文字列(フォーマット文字列)とvalue文字列(時刻を表す文字列)を基に、time.Time型の値を生成します。layout文字列は、Go言語独自の参照時刻(Mon Jan 2 15:04:05 MST 2006)を使用して、value文字列の各要素が何を表すかを定義します。

例えば、time.Parse("3:04pm", "3:04pm")のように、レイアウトに年情報が含まれていない場合、time.Parse()は不足している要素(年、月、日など)に対してデフォルト値を適用します。

time.Timeのゼロ値

Go言語では、すべての型に「ゼロ値」があります。time.Time型のゼロ値はtime.Time{}で、これは通常、time.Unix(0, 0)が返すUnixエポック(1970年1月1日 00:00:00 UTC)とは異なり、Go言語の内部表現で「1年1月1日 00:00:00 UTC」に対応します。これは、time.Timeが内部的にUnixエポックからの経過時間をナノ秒単位で保持しているためですが、その基準点がUnixエポックとは異なるためです。

UTC (Coordinated Universal Time)

UTCは協定世界時(Coordinated Universal Time)の略で、世界の標準時刻です。タイムゾーンの基準となり、多くのシステムで内部的に時刻を扱う際に使用されます。

技術的詳細

このコミットの技術的詳細は、time.Parse()関数がレイアウトに年情報がない場合に「年0」をデフォルトとして使用するという挙動と、Go言語のtime.Time型の「ゼロ値」との関係性にあります。

  1. time.Parse()のデフォルト挙動: time.Parse()は、レイアウト文字列に年、月、日などの情報が欠けている場合、それらの要素にデフォルト値を割り当てます。年に関しては、デフォルトで「年0」が使用されます。これは、例えば"3:04pm"のような時刻のみの文字列をパースする場合に顕著です。この場合、パース結果は「年0、1月1日、15:04:00 UTC」となります。

  2. time.Timeのゼロ値との比較: Go言語のtime.Time型のゼロ値は、time.Time{}で表現され、これは「1年1月1日 00:00:00 UTC」に対応します。 問題は、多くのユーザーが「年0」の時刻が、time.Timeのゼロ値(1年1月1日)と同じか、あるいはそれ以降であると直感的に考えてしまう点です。しかし、実際には「年0」は「1年1月1日」よりも前の時刻です。

  3. 「ゼロTimeより前」の意味: time.Time型は、Before()After()Equal()などのメソッドを提供しており、これらを使って時刻の比較が可能です。年0の時刻がtime.Timeのゼロ値よりも前であるということは、例えばparsedTime.Before(time.Time{})trueを返すことを意味します。 この挙動は、特にtime.Time型の初期値チェックや、時刻範囲のバリデーションを行う際に重要になります。もしユーザーが年0の時刻を有効な(ゼロ値以降の)時刻として扱ってしまうと、予期せぬバグや論理エラーにつながる可能性があります。

このコミットは、この微妙だが重要な違いをドキュメントに明記することで、開発者がtime.Parse()の挙動を正確に理解し、適切にコードを記述できるようにすることを目的としています。

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

変更はsrc/pkg/time/format.goファイルのParse関数のドキュメンテーションコメント内で行われています。

--- a/src/pkg/time/format.go
+++ b/src/pkg/time/format.go
@@ -637,7 +637,8 @@ func skip(value, prefix string) (string, error) {
 //
 // Elements omitted from the value are assumed to be zero or, when
 // zero is impossible, one, so parsing "3:04pm" returns the time
-// corresponding to Jan 1, year 0, 15:04:00 UTC.
+// corresponding to Jan 1, year 0, 15:04:00 UTC (note that because the year is
+// 0, this time is before the zero Time).
 // Years must be in the range 0000..9999. The day of the week is checked
 // for syntax but it is otherwise ignored.
 func Parse(layout, value string) (Time, error) {

具体的には、以下の行が変更されました。

  • 変更前: // corresponding to Jan 1, year 0, 15:04:00 UTC.
  • 変更後: // corresponding to Jan 1, year 0, 15:04:00 UTC (note that because the year is // 0, this time is before the zero Time).

コアとなるコードの解説

この変更は、time.Parse()関数のドキュメンテーションコメントに、年情報が省略された場合にデフォルトで「年0」が使用されること、そしてその「年0」の時刻がGo言語のtime.Time型の「ゼロ値」(time.Time{}、通常は1年1月1日)よりも前の時刻であるという重要な注意書きを追加しています。

  • note that because the year is 0, this time is before the zero Time: この追加されたフレーズが、変更の核心です。これは、time.Parse()が生成する年0の時刻が、time.Time型のデフォルトのゼロ値(1年1月1日)よりも過去の時刻であることを明確に示しています。 これにより、開発者は、年情報が欠落した時刻文字列をパースした場合に、返されるtime.Timeオブジェクトが、time.Time{}と比較してBefore()メソッドでtrueを返す可能性があることを認識できます。これは、時刻の比較ロジックや、time.Timeの初期値チェックを行う際に非常に重要です。

このドキュメントの改善により、time.Parse()の挙動に関する潜在的な誤解が解消され、より堅牢なコードの記述が促進されます。

関連リンク

参考にした情報源リンク