[インデックス 10545] ファイルの概要
このコミットは、Go言語の標準ライブラリである time
パッケージの大幅な再設計と機能強化を目的としています。具体的には、時間、期間、タイムゾーン情報を扱うための新しい型である Time
、Duration
、ZoneInfo
(後に Location
に改名)が導入されています。これにより、より正確で堅牢な時間計算とタイムゾーン処理が可能になります。
コミット
commit efe3d35fc590bf8b439f56070aa1f070125c6e8e
Author: Russ Cox <rsc@golang.org>
Date: Wed Nov 30 11:59:44 2011 -0500
time: new Time, Duration, ZoneInfo types
R=r, bradfitz, gri, dsymonds, iant
CC=golang-dev
https://golang.org/cl/5392041
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/efe3d35fc590bf8b439f56070aa1f070125c6e8e
元コミット内容
このコミットの目的は、Go言語の time
パッケージに新しい Time
、Duration
、ZoneInfo
型を導入することです。これにより、時間の表現、期間の計算、およびタイムゾーン情報の管理が改善されます。
変更の背景
Go言語は2011年当時、まだ初期開発段階にあり、標準ライブラリの設計と実装が活発に行われていました。time
パッケージもその一つで、初期の実装では時間の表現やタイムゾーンの扱いにおいて、いくつかの課題を抱えていました。
具体的な背景としては、以下のような点が挙げられます。
- 正確性と堅牢性の向上: 既存の時間表現では、ナノ秒単位の精度や、異なるタイムゾーン間での正確な時間比較・計算が困難でした。特に、うるう秒や夏時間(DST)の考慮など、複雑な時間計算に対応できる堅牢な基盤が必要とされていました。
- APIの明確化と使いやすさ: 時間の「瞬間」と「期間」を明確に区別し、それぞれに特化した型を導入することで、APIの意図をより明確にし、開発者が直感的に時間を扱えるようにすることが目指されました。
- タイムゾーン処理の改善: タイムゾーンの概念は、国際的なアプリケーション開発において不可欠です。しかし、その処理は非常に複雑であり、Goの
time
パッケージがこれを適切に扱うための専用のメカニズムが必要でした。特に、タイムゾーンのオフセットだけでなく、タイムゾーン名や夏時間のルールを管理できるLocation
(旧ZoneInfo
)型の導入が求められました。 - 内部実装の効率化: 時間計算の内部ロジックを最適化し、パフォーマンスを向上させることも重要な目標でした。特に、システムコールを介した現在時刻の取得を効率化するため、アセンブリレベルでの最適化も行われています。
これらの背景から、time
パッケージは、より現代的で、正確かつ堅牢な時間処理を提供するために、この大規模な再設計が行われました。
前提知識の解説
このコミットを理解するためには、以下の概念が重要です。
- Unixエポック (Unix Epoch): 1970年1月1日00:00:00 UTC(協定世界時)を基準点(エポック)とし、そこからの経過秒数(またはミリ秒、ナノ秒)で時間を表現する方法です。多くのシステムで内部的な時間表現に用いられます。
- UTC (協定世界時): 世界の標準時であり、原子時計に基づいて決定されます。タイムゾーンの基準となる時間です。
- タイムゾーン (Time Zone): 地球上の特定の地域で採用されている標準時です。UTCからのオフセット(時差)と、夏時間(Daylight Saving Time, DST)のルールによって定義されます。
- 夏時間 (Daylight Saving Time, DST): 特定の期間に時間を1時間進める制度です。これにより、タイムゾーンのオフセットが季節によって変動します。
- アセンブリ言語 (Assembly Language): コンピュータのプロセッサが直接実行できる機械語にほぼ1対1で対応する低水準プログラミング言語です。OSのシステムコールを直接呼び出すなど、パフォーマンスが重視される部分や、ハードウェアに密接な処理で用いられます。
- システムコール (System Call): オペレーティングシステム(OS)のカーネルが提供するサービスを、ユーザープログラムが利用するためのインターフェースです。例えば、現在時刻の取得 (
gettimeofday
) などがあります。 strconv
パッケージ: Go言語の標準ライブラリで、文字列と数値の相互変換を提供するパッケージです。
技術的詳細
このコミットの技術的な詳細は多岐にわたりますが、主要な変更点は以下の通りです。
-
Time
構造体の再定義:- 以前の
Time
構造体は、年、月、日、時、分、秒、ナノ秒、タイムゾーンオフセット、タイムゾーン名といったフィールドを直接持っていました。 - 新しい
Time
構造体は、sec
(int64)、nsec
(int32)、loc
(*Location) の3つのフィールドに集約されました。sec
: 1年1月1日00:00:00 UTCからの経過秒数を表すint64
。nsec
:sec
で示される秒内のナノ秒オフセットを表すint32
(0から999,999,999の範囲)。loc
: このTime
の表示形式(年、月、時など)を決定するために使用される*Location
へのポインタ。nil
の場合はUTCと解釈されます。
- この変更により、時間の内部表現がよりコンパクトになり、タイムゾーンに依存しない絶対的な時間瞬間を正確に表現できるようになりました。
After
,Before
,Equal
,IsZero
といった比較メソッドが導入され、Time
オブジェクトの比較が容易になりました。Date()
,Year()
,Month()
,Day()
,Weekday()
,ISOWeek()
,Clock()
,Hour()
,Minute()
,Second()
,Nanosecond()
といった、Time
オブジェクトから日付や時刻の各要素を取得するメソッドが追加されました。これらのメソッドは、loc
フィールドに基づいて適切なタイムゾーンでの表示を計算します。
- 以前の
-
Duration
型の導入:- 時間の「期間」を表す
Duration
型がint64
のエイリアスとして導入されました。これはナノ秒単位で期間を表現します。 Nanosecond
,Microsecond
,Millisecond
,Second
,Minute
,Hour
といった期間を表す定数が追加されました。Duration.String()
メソッドが追加され、期間を人間が読みやすい形式(例: "72h3m0.5s")で出力できるようになりました。
- 時間の「期間」を表す
-
Location
型(旧ZoneInfo
)の導入:- タイムゾーン情報をカプセル化するための
Location
型が導入されました(コミットメッセージではZoneInfo
と記載されていますが、コードではLocation
となっています)。 - これにより、タイムゾーンのオフセットだけでなく、タイムゾーン名や夏時間のルールを管理できるようになり、複雑なタイムゾーン処理を
time
パッケージが内部で処理できるようになりました。 time.LoadLocation
やtime.FixedZone
といった関数を通じてLocation
オブジェクトを取得し、Time.In
メソッドで特定のタイムゾーンでのTime
オブジェクトを生成できるようになります。
- タイムゾーン情報をカプセル化するための
-
Format
およびParse
メソッドの改善:Time.Format
メソッドは、新しいTime
構造体とLocation
型に対応するように全面的に書き直されました。これにより、タイムゾーン情報を含む時間のフォーマットがより正確に行えるようになりました。Parse
関数も同様に、新しいTime
構造体とLocation
型に対応し、様々なフォーマットの文字列からTime
オブジェクトを正確にパースできるようになりました。特に、タイムゾーンの解析ロジックが強化されています。strconv
パッケージへの依存を避けるため、itoa
(integer to ASCII) とatoi
(ASCII to integer) の簡易実装がformat.go
内に直接追加されました。これは、time
パッケージがコアライブラリであり、依存関係を最小限に抑えるための設計判断と考えられます。
-
ランタイム(アセンブリ)レベルでの時刻取得の最適化:
src/pkg/runtime
以下の各OS/アーキテクチャ固有のアセンブリファイル(例:darwin/386/sys.s
,linux/amd64/sys.s
など)に、time·now
という関数が追加されました。- この
time·now
関数は、OSのシステムコール(例:gettimeofday
)を直接呼び出し、現在の時刻を秒 (sec int64
) とナノ秒 (nsec int32
) で取得します。 - これにより、Goのランタイムが直接、高精度な現在時刻を取得できるようになり、
time
パッケージのパフォーマンスが向上しました。
-
テストコードの追加と更新:
example_test.go
が新規追加され、Duration
,After
,Sleep
,Tick
,Month
,Date
といった新しいAPIの使用例が示されました。- 既存のテストファイル(
sleep_test.go
,tick_test.go
など)も、新しいTime
およびDuration
型を使用するように更新されました。
これらの変更は、Go言語が提供する時間処理の能力を飛躍的に向上させ、より複雑な時間関連の要件を持つアプリケーションの開発を可能にしました。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は以下のファイルに集中しています。
src/pkg/time/time.go
:Time
構造体の定義、Month
およびWeekday
型の導入、Duration
型の導入、そして時間計算と要素取得に関する多数のメソッド(After
,Before
,Equal
,IsZero
,Date
,Year
,Month
,Day
,Weekday
,ISOWeek
,Clock
,Hour
,Minute
,Second
,Nanosecond
)の追加・変更。src/pkg/time/format.go
:Time.Format
メソッドとParse
関数の大幅な改修。itoa
およびatoi
関数の追加。src/pkg/time/sleep.go
:NewTimer
,After
関数がDuration
型を受け取るように変更され、Timer.C
がTime
型を送信するように変更。src/pkg/time/tick.go
:NewTicker
,Tick
関数がDuration
型を受け取るように変更され、Ticker.C
がTime
型を送信するように変更。src/pkg/time/sys.go
:Seconds()
およびNanoseconds()
関数の削除、Sleep
関数のシグネチャ変更。src/pkg/runtime/*/sys.s
(例:src/pkg/runtime/darwin/386/sys.s
): 各OS/アーキテクチャ向けにtime·now
アセンブリ関数の追加。src/pkg/time/zoneinfo.go
:Location
型(旧ZoneInfo
)の定義とタイムゾーン情報管理ロジックの追加(コミットの差分からは全体像が見えませんが、新規ファイルとして追加されています)。
コアとなるコードの解説
src/pkg/time/time.go
の変更
最も重要な変更は Time
構造体の再定義です。
type Time struct {
// sec gives the number of seconds elapsed since
// January 1, year 1 00:00:00 UTC.
sec int64
// nsec specifies a non-negative nanosecond
// offset within the second named by Seconds.
// It must be in the range [0, 999999999].
nsec int32
// loc specifies the Location that should be used to
// determine the minute, hour, month, day, and year
// that correspond to this Time.
// Only the zero Time has a nil Location.
// In that case it is interpreted to mean UTC.
loc *Location
}
この新しい構造体は、時間を「絶対的な瞬間」として sec
と nsec
で表現し、その「表示方法」を loc
(Location) で分離しています。これにより、タイムゾーンに依存しない時間計算が可能になり、表示時にのみタイムゾーンのルールが適用されるようになります。
また、Duration
型が int64
のエイリアスとして導入され、時間の期間をナノ秒単位で統一的に扱えるようになりました。
type Duration int64
Time
型に多数のメソッドが追加されたことで、日付や時刻の各要素を簡単に取得できるようになりました。例えば、t.Year()
, t.Month()
, t.Day()
などです。これらのメソッドは内部で loc
フィールドを参照し、適切なタイムゾーンでの値を返します。
src/pkg/time/format.go
の変更
Time.Format
メソッドは、新しい Time
構造体と Location
型に対応するために、内部のロジックが大きく変更されました。特に、年、月、日、時、分、秒、タイムゾーンの各要素を Time
オブジェクトから取得し、指定されたレイアウト文字列に従ってフォーマットする処理が再構築されています。
Parse
関数も同様に、入力文字列を解析して新しい Time
構造体のフィールドにマッピングするロジックが強化されました。タイムゾーンの解析もより柔軟に行えるようになっています。
itoa
と atoi
の導入は、time
パッケージが strconv
パッケージに依存しないようにするためのものです。これは、Goの標準ライブラリにおける依存関係の最小化という設計原則に基づいています。
src/pkg/runtime/*/sys.s
の変更
各OS/アーキテクチャのアセンブリファイルに追加された time·now
関数は、Goの time
パッケージがシステムから現在時刻を直接、かつ高精度に取得するための重要な部分です。例えば、Linux/amd64の場合、gettimeofday
システムコールを呼び出して、秒とマイクロ秒を取得し、それをナノ秒に変換して返します。
// func now() (sec int64, nsec int32)
TEXT time·now(SB), 7, $32
LEAQ 8(SP), DI
MOVQ $0, SI
MOVQ $0xffffffffff600000, AX
CALL AX
MOVQ 8(SP), AX // sec
MOVL 16(SP), DX // usec
// sec is in AX, usec in DX
MOVQ AX, sec+0(FP)
IMULQ $1000, DX
MOVL DX, nsec+8(FP)
RET
この低レベルな実装により、time
パッケージはOSの提供する最も正確な時刻情報を利用できるようになっています。
これらの変更は、Go言語の time
パッケージが、単なる時刻表示ツールから、複雑な時間計算とタイムゾーン処理を正確かつ効率的に行える堅牢なライブラリへと進化するための基盤を築きました。
関連リンク
- Go言語の
time
パッケージ公式ドキュメント: https://pkg.go.dev/time - Go言語の
time
パッケージの設計に関する議論(Goのメーリングリストなど)
参考にした情報源リンク
- Go言語の
time
パッケージの設計に関する情報源(Web検索結果より)