[インデックス 1780] ファイルの概要
このコミットは、Go言語の初期のtime
パッケージにおけるドキュメンテーションの改善と、内部エラー変数の可視性調整を目的としています。具体的には、sleep.go
、tick.go
、time.go
、zoneinfo.go
の各ファイルにおいて、関数、型、定数に対するコメントが追加・修正され、APIの意図や振る舞いがより明確に記述されています。また、zoneinfo.go
では、パッケージ内部でのみ使用されるエラー変数が、外部からアクセスできないように非エクスポート(unexported)化されています。これにより、パッケージのAPIが整理され、利用者が混乱することなくtime
パッケージの機能を利用できるようになります。
コミット
commit c5560d3aaae55e266beb2cb96049f769006bbbf7
Author: Rob Pike <r@golang.org>
Date: Sat Mar 7 16:56:05 2009 -0800
document time
R=rsc
DELTA=42 (23 added, 1 deleted, 18 changed)
OCL=25881
CL=25886
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c5560d3aaae55e266beb2cb96049f769006bbbf7
元コミット内容
document time
このコミットの主な目的は、Go言語のtime
パッケージに関するドキュメンテーションを整備することです。
変更の背景
Go言語は、その設計思想として「シンプルさ」と「明確さ」を重視しています。初期の段階では、機能の実装が優先され、ドキュメンテーションが追いついていない部分がありました。このコミットが行われた2009年3月は、Go言語がまだ一般に公開される前の開発初期段階であり、言語のAPIや標準ライブラリの設計が固まりつつある時期でした。
このような時期において、time
パッケージのような基本的なライブラリのAPIが明確にドキュメント化されていることは、将来の利用者にとって非常に重要です。適切なドキュメンテーションは、APIの利用方法を明確にし、誤用を防ぎ、コードの可読性と保守性を向上させます。
また、zoneinfo.go
におけるエラー変数の非エクスポート化は、Go言語の「エクスポートされた名前は常にコメントを持つべきである」という慣習と、「内部実装の詳細は隠蔽すべきである」という原則に基づいています。パッケージ内部でのみ使用されるエラー変数を非エクスポートにすることで、パッケージの外部インターフェースがよりクリーンになり、利用者が不必要に内部の詳細に依存することを防ぎます。
前提知識の解説
このコミットの変更内容を理解するためには、以下のGo言語の基本的な概念と、時間に関する一般的な知識が必要です。
-
Go言語のパッケージとエクスポート/非エクスポート:
- Go言語では、コードはパッケージにまとめられます。
- 識別子(変数、関数、型など)の最初の文字が大文字の場合、その識別子はパッケージ外からアクセス可能(エクスポート)になります。
- 最初の文字が小文字の場合、その識別子はパッケージ内でのみアクセス可能(非エクスポート)になります。
- このコミットでは、
zoneinfo.go
内のエラー変数BadZoneinfo
とNoZoneinfo
が、それぞれbadZoneinfo
とnoZoneinfo
に名前が変更され、非エクスポート化されています。これは、これらのエラーがパッケージの内部的な問題を示すものであり、外部のコードが直接参照する必要がないためです。
-
time
パッケージ:- Go言語の標準ライブラリの一部であり、時間に関する機能を提供します。
- 時間の測定、表示、フォーマット、タイムゾーンの処理などを行います。
time.Time
型は、特定の時点を表す構造体です。time.Duration
型は、時間の長さを表します。
-
goroutine
:- Go言語における軽量な並行処理の単位です。
- OSのスレッドよりもはるかに軽量で、数千、数万のgoroutineを同時に実行できます。
Sleep
関数は、現在のgoroutineの実行を一時停止するために使用されます。
-
syscall
パッケージ:- オペレーティングシステム(OS)のシステムコールにアクセスするための機能を提供します。
syscall.Timeval
は、秒とマイクロ秒で時間を表す構造体で、システムコールで時間を扱う際によく使用されます。syscall.Nstotimeval
は、ナノ秒をTimeval
構造体に変換する関数です。
-
os.Error
:- Go言語の初期バージョンで使用されていたエラー型です。現在の
error
インターフェースの前身にあたります。 os.NewError
は、新しいエラーインスタンスを作成するために使用されます。
- Go言語の初期バージョンで使用されていたエラー型です。現在の
-
chan
(チャネル):- Go言語におけるgoroutine間の通信手段です。
- チャネルを通じて値を送受信することで、goroutine間で安全にデータを共有し、同期を取ることができます。
Tick
関数は、定期的に時間情報を送信するチャネルを作成します。
-
Unix Epoch (Unixエポック):
- 1970年1月1日00:00:00 UTCを基準点とする時刻の表現方法です。
- この基準点からの経過秒数または経過ナノ秒数で時間を表します。
Seconds()
やNanoseconds()
関数は、このUnixエポックからの経過時間を返します。
-
TZif (Time Zone Information Format):
- タイムゾーン情報を格納するための標準的なバイナリフォーマットです。
- 通常、
/usr/share/zoneinfo/
ディレクトリに配置されるタイムゾーンデータファイル(例:America/New_York
)は、このTZif形式で記述されています。 zoneinfo.go
は、これらのTZifファイルを解析してタイムゾーン情報を取得する役割を担っています。
技術的詳細
このコミットは、主にGo言語のtime
パッケージ内の公開APIと内部実装に対するドキュメンテーションの追加・修正、および内部エラー変数の可視性変更に焦点を当てています。
-
src/lib/time/sleep.go
:Sleep
関数にコメントが追加されました。// Sleep pauses the current goroutine for ns nanoseconds. // It returns os.EINTR if interrupted. func Sleep(ns int64) *os.Error {
- これにより、
Sleep
関数が現在のgoroutineを指定されたナノ秒間一時停止させること、および中断された場合にos.EINTR
エラーを返す可能性があることが明確に示されました。
-
src/lib/time/tick.go
:Tick
関数にコメントが追加されました。// Tick creates a synchronous channel that will send the time, in nanoseconds, // every ns nanoseconds. It adjusts the intervals to make up for pauses in // delivery of the ticks. func Tick(ns int64) chan int64 {
- このコメントは、
Tick
関数が指定されたナノ秒間隔で時間(ナノ秒単位)を送信する同期チャネルを作成すること、そしてティックの配信における一時停止を補うために間隔を調整するメカニズムがあることを説明しています。
-
src/lib/time/time.go
:- パッケージ全体のコメントが追加されました。
// The time package provides functionality for measuring and // displaying time. package time
Seconds()
関数とNanoseconds()
関数のコメントが修正され、より明確になりました。Seconds()
:// Seconds reports the number of seconds since the Unix epoch, // January 1, 1970 00:00:00 UTC.
Nanoseconds()
:// Nanoseconds reports the number of nanoseconds since the Unix epoch, // January 1, 1970 00:00:00 UTC.
Days of the week
定数ブロックにコメントが追加されました。// Days of the week. const ( Sunday = iota; // ... )
Time
構造体の定義にコメントが追加され、各フィールドの役割が明確化されました。特にWeekday
フィールドのコメントが// Sunday, Monday, ...
と修正され、具体的な値ではなく意味が示されました。// Time is the struct representing a parsed time value. type Time struct { Year int64; // 2008 is 2008 Month, Day int; // Sep-17 is 9, 17 Hour, Minute, Second int; // 10:43:12 is 10, 43, 12 Weekday int; // Sunday, Monday, ... ZoneOffset int; // seconds west of UTC Zone string; }
SecondsToUTC
、UTC
、SecondsToLocalTime
、LocalTime
、Time.Seconds()
メソッドにコメントが追加され、それぞれの機能が明確に記述されました。SecondsToUTC
:// SecondsToUTC converts sec, in number of seconds since the Unix epoch, // into a parsed Time value in the UTC time zone.
UTC
:// UTC returns the current time as a parsed Time value in the UTC time zone.
SecondsToLocalTime
:// SecondsToLocalTime converts sec, in number of seconds since the Unix epoch, // into a parsed Time value in the local time zone.
LocalTime
:// LocalTime returns the current time as a parsed Time value in the local time zone.
Time.Seconds()
:// Seconds returns the number of seconds since January 1, 1970 represented by the // parsed Time value.
SecondsToLocalTime
関数内で、time.LookupTimezone
がtime.lookupTimezone
に変更されました。これは、lookupTimezone
がパッケージ内部関数であることを示唆しており、外部から直接呼び出されるべきではないことを意味します。Asctime
、RFC850
、RFC1123
、String
といった時間フォーマットメソッドにコメントが追加され、それぞれの出力形式が明確に示されました。
- パッケージ全体のコメントが追加されました。
-
src/lib/time/zoneinfo.go
:- エラー変数
BadZoneinfo
とNoZoneinfo
が、それぞれbadZoneinfo
とnoZoneinfo
にリネームされ、非エクスポート化されました。// Errors that can be generated recovering time zone information. var ( badZoneinfo = os.NewError("time: malformed zoneinfo"); noZoneinfo = os.NewError("time: unknown time zone") )
- これにより、これらのエラーがパッケージ内部でのみ使用されることが明確になり、外部からの直接参照が不可能になりました。
parseinfo
関数やreadfile
関数内で、これらのエラー変数の参照が新しい非エクスポート名に更新されました。LookupTimezone
関数がlookupTimezone
にリネームされ、非エクスポート化されました。これは、この関数がパッケージ内部でのみ使用されるべきであることを示しています。
- エラー変数
全体として、このコミットはGo言語のtime
パッケージのAPIドキュメンテーションを大幅に改善し、利用者がより簡単に、かつ正確にパッケージの機能を使用できるようにするための重要なステップです。また、内部エラー変数の非エクスポート化は、Go言語の設計原則に沿ったものであり、パッケージの健全性を高めます。
コアとなるコードの変更箇所
このコミットでは、以下の4つのファイルが変更されています。
src/lib/time/sleep.go
src/lib/time/tick.go
src/lib/time/time.go
src/lib/time/zoneinfo.go
主な変更内容は、既存の関数、型、定数に対するコメントの追加・修正、およびzoneinfo.go
における内部エラー変数と関数の非エクスポート化です。
コアとなるコードの解説
src/lib/time/sleep.go
--- a/src/lib/time/sleep.go
+++ b/src/lib/time/sleep.go
@@ -10,6 +10,8 @@ import (
"unsafe";
)
+// Sleep pauses the current goroutine for ns nanoseconds.
+// It returns os.EINTR if interrupted.
func Sleep(ns int64) *os.Error {
var tv syscall.Timeval;
syscall.Nstotimeval(ns, &tv);
Sleep
関数に、その機能(現在のgoroutineをナノ秒間一時停止させる)と、中断された場合にos.EINTR
エラーを返す可能性があるという重要な情報が追加されました。これにより、関数の振る舞いが明確になります。
src/lib/time/tick.go
--- a/src/lib/time/tick.go
+++ b/src/lib/time/tick.go
@@ -48,6 +48,9 @@ func ticker(ns int64, c chan int64) {
}
}
+// Tick creates a synchronous channel that will send the time, in nanoseconds,
+// every ns nanoseconds. It adjusts the intervals to make up for pauses in
+// delivery of the ticks.
func Tick(ns int64) chan int64 {
if ns <= 0 {
return nil
Tick
関数に、同期チャネルを作成し、指定された間隔でナノ秒単位の時間を送信すること、そしてティックの配信における一時停止を補うために間隔を調整するメカニズムがあることが説明されました。これは、Tick
関数の高度な振る舞いを理解する上で不可欠な情報です。
src/lib/time/time.go
--- a/src/lib/time/time.go
+++ b/src/lib/time/time.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// The time package provides functionality for measuring and
+// displaying time.
package time
import (
@@ -9,7 +11,8 @@ import (
"time"
)
-// Seconds since January 1, 1970 00:00:00 UTC
+// Seconds reports the number of seconds since the Unix epoch,
+// January 1, 1970 00:00:00 UTC.
func Seconds() int64 {
sec, nsec, err := os.Time();
if err != nil {
@@ -18,7 +21,8 @@ func Seconds() int64 {
return sec
}
-// Nanoseconds since January 1, 1970 00:00:00 UTC
+// Nanoseconds reports the number of nanoseconds since the Unix epoch,
+// January 1, 1970 00:00:00 UTC.
func Nanoseconds() int64 {
sec, nsec, err := os.Time();
if err != nil {
@@ -27,6 +31,7 @@ func Nanoseconds() int64 {
return sec*1e9 + nsec
}
+// Days of the week.
const (
Sunday = iota;
Monday;
@@ -37,11 +42,12 @@ const (
Saturday;
)
+// Time is the struct representing a parsed time value.
type Time struct {
Year int64; // 2008 is 2008
Month, Day int; // Sep-17 is 9, 17
Hour, Minute, Second int; // 10:43:12 is 10, 43, 12
-\tWeekday int; // Sunday = 0, Monday = 1, ...
+\tWeekday int; // Sunday, Monday, ...
ZoneOffset int; // seconds west of UTC
Zone string;
}
@@ -70,6 +76,8 @@ const (
\tdays1970To2001 = 31*365+8;
)
+// SecondsToUTC converts sec, in number of seconds since the Unix epoch,
+// into a parsed Time value in the UTC time zone.
func SecondsToUTC(sec int64) *Time {
\tt := new(Time);
@@ -143,12 +151,15 @@ func SecondsToUTC(sec int64) *Time {\n \treturn t;\n }\n \n+// UTC returns the current time as a parsed Time value in the UTC time zone.\n func UTC() *Time {\n \treturn SecondsToUTC(Seconds())\n }\n \n+// SecondsToLocalTime converts sec, in number of seconds since the Unix epoch,
+// into a parsed Time value in the local time zone.\n func SecondsToLocalTime(sec int64) *Time {\
-\tz, offset, err := time.LookupTimezone(sec);\n+\tz, offset, err := time.lookupTimezone(sec);\n \tif err != nil {\n \t\treturn SecondsToUTC(sec)\n \t}\n@@ -158,11 +169,13 @@ func SecondsToLocalTime(sec int64) *Time {\n \treturn t\n }\n \n+// LocalTime returns the current time as a parsed Time value in the local time zone.\n func LocalTime() *Time {\
\treturn SecondsToLocalTime(Seconds())\n }\n \n-// Compute number of seconds since January 1, 1970.\n+// Seconds returns the number of seconds since January 1, 1970 represented by the
+// parsed Time value.\n func (t *Time) Seconds() int64 {\
\t// First, accumulate days since January 1, 2001.\n \t// Using 2001 instead of 1970 makes the leap-year\n@@ -334,23 +347,26 @@ func format(t *Time, fmt string) string {\n \treturn string(buf[0:bp])\n }\n \n+// Asctime formats the parsed time value in the style of\n // ANSI C asctime: Sun Nov 6 08:49:37 1994\n func (t *Time) Asctime() string {\
\treturn format(t, "%a %b %e %H:%M:%S %Y")\n }\n \n+// RFC850 formats the parsed time value in the style of\n // RFC 850: Sunday, 06-Nov-94 08:49:37 UTC\n func (t *Time) RFC850() string {\
\treturn format(t, "%A, %d-%b-%y %H:%M:%S %Z")\n }\n \n+// RFC1123 formats the parsed time value in the style of\n // RFC 1123: Sun, 06 Nov 1994 08:49:37 UTC\n func (t *Time) RFC1123() string {\
\treturn format(t, "%a, %d %b %Y %H:%M:%S %Z")\n }\n \n+// String formats the parsed time value in the style of\n // date(1) - Sun Nov 6 08:49:37 UTC 1994\n func (t *Time) String() string {\
\treturn format(t, "%a %b %e %H:%M:%S %Z %Y")\n }\n```
このファイルは、`time`パッケージの主要な機能とデータ構造を定義しています。
* パッケージ全体の目的を説明するコメントが追加されました。
* `Seconds()`と`Nanoseconds()`関数のコメントが、Unixエポックからの経過秒数/ナノ秒数を報告することを明確にしました。
* `Days of the week`定数ブロックにコメントが追加され、その目的が示されました。
* `Time`構造体には、その役割(解析された時間値を表す)と、各フィールドの具体的な意味(例: `Weekday`が`Sunday, Monday, ...`を表すこと)がコメントで追加されました。
* `SecondsToUTC`、`UTC`、`SecondsToLocalTime`、`LocalTime`、`Time.Seconds()`といった時間変換・取得メソッドに詳細なコメントが追加され、それぞれの機能と返される値の性質が明確になりました。
* `SecondsToLocalTime`内で`time.LookupTimezone`が`time.lookupTimezone`に変更されたのは、この関数がパッケージ内部でのみ使用されるべきであることを示唆しています。これは、Goの慣習に従い、内部実装の詳細を隠蔽するための変更です。
* `Asctime`、`RFC850`、`RFC1123`、`String`といった時間フォーマットメソッドには、それぞれが生成する文字列の具体的なフォーマット例がコメントで示されました。これにより、これらのメソッドの利用者が期待する出力形式を容易に理解できるようになります。
### `src/lib/time/zoneinfo.go`
```diff
--- a/src/lib/time/zoneinfo.go
+++ b/src/lib/time/zoneinfo.go
@@ -22,9 +22,10 @@ const (
zoneDir = "/usr/share/zoneinfo/";
)
+// Errors that can be generated recovering time zone information.
var (
-\tBadZoneinfo = os.NewError("time: malformed zoneinfo");
-\tNoZoneinfo = os.NewError("time: unknown time zone")
+\tbadZoneinfo = os.NewError("time: malformed zoneinfo");
+\tnoZoneinfo = os.NewError("time: unknown time zone")
)
// Simple I/O interface to binary blob of data.
@@ -92,13 +92,13 @@ func parseinfo(bytes []byte) (zt []zonetime, err *os.Error) {\
// 4-byte magic "TZif"
if magic := d.read(4); string(magic) != "TZif" {
-\t\treturn nil, BadZoneinfo
+\t\treturn nil, badZoneinfo
}
// 1-byte version, then 15 bytes of padding
var p []byte;
if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' {
-\t\treturn nil, BadZoneinfo
+\t\treturn nil, badZoneinfo
}
vers := p[0];
@@ -121,7 +122,7 @@ func parseinfo(bytes []byte) (zt []zonetime, err *os.Error) {\
for i := 0; i < 6; i++ {
nn, ok := d.big4();
if !ok {
-\t\t\treturn nil, BadZoneinfo
+\t\t\treturn nil, badZoneinfo
}
n[i] = int(nn);\
}\
@@ -150,7 +151,7 @@ func parseinfo(bytes []byte) (zt []zonetime, err *os.Error) {\
isutc := d.read(n[NUTCLocal]);
if d.error { // ran out of data
-\t\treturn nil, BadZoneinfo
+\t\treturn nil, badZoneinfo
}\
// If version == 2, the entire file repeats, this time using
@@ -165,16 +166,16 @@ func parseinfo(bytes []byte) (zt []zonetime, err *os.Error) {\
var ok bool;\
var n uint32;\
if n, ok = zonedata.big4(); !ok {
-\t\t\treturn nil, BadZoneinfo
+\t\t\treturn nil, badZoneinfo
}\
z[i].utcoff = int(n);\
var b byte;\
if b, ok = zonedata.byte(); !ok {
-\t\t\treturn nil, BadZoneinfo
+\t\t\treturn nil, badZoneinfo
}\
z[i].isdst = b != 0;\
if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
-\t\t\treturn nil, BadZoneinfo
+\t\t\treturn nil, badZoneinfo
}\
z[i].name = byteString(abbrev[b:len(abbrev)])
}\
@@ -185,11 +186,11 @@ func parseinfo(bytes []byte) (zt []zonetime, err *os.Error) {\
var ok bool;\
var n uint32;\
if n, ok = txtimes.big4(); !ok {
-\t\t\treturn nil, BadZoneinfo
+\t\t\treturn nil, badZoneinfo
}\
zt[i].time = int32(n);\
if int(txzones[i]) >= len(z) {
-\t\t\treturn nil, BadZoneinfo
+\t\t\treturn nil, badZoneinfo
}\
zt[i].zone = &z[txzones[i]];
if i < len(isstd) {
@@ -211,7 +212,7 @@ func readfile(name string, max int) (p []byte, err *os.Error) {\
n, err1 := io.Readn(fd, p);\
fd.Close();\
if err1 == nil { // too long
-\t\treturn nil, BadZoneinfo;\
+\t\treturn nil, badZoneinfo;\
}\
if err1 != io.ErrEOF {\
return nil, err1;\
@@ -251,7 +252,7 @@ func setupZone() {\
}\
}\
-func LookupTimezone(sec int64) (zone string, offset int, err *os.Error) {\
+func lookupTimezone(sec int64) (zone string, offset int, err *os.Error) {\
\tonce.Do(setupZone);\
\tif zoneerr != nil || len(zones) == 0 {\
\t\treturn "UTC", 0, zoneerr
このファイルは、タイムゾーン情報の読み込みと解析を担当しています。
- 最も重要な変更は、エクスポートされていたエラー変数
BadZoneinfo
とNoZoneinfo
が、それぞれbadZoneinfo
とnoZoneinfo
にリネームされ、非エクスポート化されたことです。これにより、これらのエラーはパッケージ内部でのみ使用されるようになり、外部のコードがこれらの内部エラーに直接依存することを防ぎます。これは、GoのAPI設計における重要な原則の一つである「内部実装の隠蔽」に従ったものです。 - 同様に、
LookupTimezone
関数もlookupTimezone
にリネームされ、非エクスポート化されました。これは、この関数がタイムゾーン情報の内部的な検索ロジックをカプセル化しており、外部から直接呼び出されるべきではないことを示しています。 - これらの名前変更に伴い、
parseinfo
関数やreadfile
関数内のエラー参照も新しい非エクスポート名に更新されました。
これらの変更は、Go言語のtime
パッケージのAPIをよりクリーンで使いやすくし、内部実装の詳細を適切に隠蔽することで、パッケージの保守性と安定性を向上させることに貢献しています。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/doc/
- Go言語の
time
パッケージのドキュメント: https://pkg.go.dev/time (現在のバージョン)
参考にした情報源リンク
- Go言語のソースコード (GitHub): https://github.com/golang/go
- Go言語の設計原則に関する情報 (Goブログなど): https://go.dev/blog/
- TZif (Time Zone Information Format) の仕様: https://www.iana.org/time-zones/repository/tz-how-to.html (IANA Time Zone Database)
- Go言語のエラーハンドリングに関する情報: https://go.dev/blog/error-handling-and-go (現在のエラーハンドリングの慣習)
- Go言語の初期のコミット履歴 (GitHub): https://github.com/golang/go/commits/master