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

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

このコミットは、Go言語の標準ライブラリである time パッケージの大幅な再設計と機能強化を目的としています。具体的には、時間、期間、タイムゾーン情報を扱うための新しい型である TimeDurationZoneInfo(後に 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 パッケージに新しい TimeDurationZoneInfo 型を導入することです。これにより、時間の表現、期間の計算、およびタイムゾーン情報の管理が改善されます。

変更の背景

Go言語は2011年当時、まだ初期開発段階にあり、標準ライブラリの設計と実装が活発に行われていました。time パッケージもその一つで、初期の実装では時間の表現やタイムゾーンの扱いにおいて、いくつかの課題を抱えていました。

具体的な背景としては、以下のような点が挙げられます。

  1. 正確性と堅牢性の向上: 既存の時間表現では、ナノ秒単位の精度や、異なるタイムゾーン間での正確な時間比較・計算が困難でした。特に、うるう秒や夏時間(DST)の考慮など、複雑な時間計算に対応できる堅牢な基盤が必要とされていました。
  2. APIの明確化と使いやすさ: 時間の「瞬間」と「期間」を明確に区別し、それぞれに特化した型を導入することで、APIの意図をより明確にし、開発者が直感的に時間を扱えるようにすることが目指されました。
  3. タイムゾーン処理の改善: タイムゾーンの概念は、国際的なアプリケーション開発において不可欠です。しかし、その処理は非常に複雑であり、Goの time パッケージがこれを適切に扱うための専用のメカニズムが必要でした。特に、タイムゾーンのオフセットだけでなく、タイムゾーン名や夏時間のルールを管理できる Location(旧 ZoneInfo)型の導入が求められました。
  4. 内部実装の効率化: 時間計算の内部ロジックを最適化し、パフォーマンスを向上させることも重要な目標でした。特に、システムコールを介した現在時刻の取得を効率化するため、アセンブリレベルでの最適化も行われています。

これらの背景から、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言語の標準ライブラリで、文字列と数値の相互変換を提供するパッケージです。

技術的詳細

このコミットの技術的な詳細は多岐にわたりますが、主要な変更点は以下の通りです。

  1. 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 フィールドに基づいて適切なタイムゾーンでの表示を計算します。
  2. Duration 型の導入:

    • 時間の「期間」を表す Duration 型が int64 のエイリアスとして導入されました。これはナノ秒単位で期間を表現します。
    • Nanosecond, Microsecond, Millisecond, Second, Minute, Hour といった期間を表す定数が追加されました。
    • Duration.String() メソッドが追加され、期間を人間が読みやすい形式(例: "72h3m0.5s")で出力できるようになりました。
  3. Location 型(旧 ZoneInfo)の導入:

    • タイムゾーン情報をカプセル化するための Location 型が導入されました(コミットメッセージでは ZoneInfo と記載されていますが、コードでは Location となっています)。
    • これにより、タイムゾーンのオフセットだけでなく、タイムゾーン名や夏時間のルールを管理できるようになり、複雑なタイムゾーン処理を time パッケージが内部で処理できるようになりました。
    • time.LoadLocationtime.FixedZone といった関数を通じて Location オブジェクトを取得し、Time.In メソッドで特定のタイムゾーンでの Time オブジェクトを生成できるようになります。
  4. Format および Parse メソッドの改善:

    • Time.Format メソッドは、新しい Time 構造体と Location 型に対応するように全面的に書き直されました。これにより、タイムゾーン情報を含む時間のフォーマットがより正確に行えるようになりました。
    • Parse 関数も同様に、新しい Time 構造体と Location 型に対応し、様々なフォーマットの文字列から Time オブジェクトを正確にパースできるようになりました。特に、タイムゾーンの解析ロジックが強化されています。
    • strconv パッケージへの依存を避けるため、itoa (integer to ASCII) と atoi (ASCII to integer) の簡易実装が format.go 内に直接追加されました。これは、time パッケージがコアライブラリであり、依存関係を最小限に抑えるための設計判断と考えられます。
  5. ランタイム(アセンブリ)レベルでの時刻取得の最適化:

    • src/pkg/runtime 以下の各OS/アーキテクチャ固有のアセンブリファイル(例: darwin/386/sys.s, linux/amd64/sys.s など)に、time·now という関数が追加されました。
    • この time·now 関数は、OSのシステムコール(例: gettimeofday)を直接呼び出し、現在の時刻を秒 (sec int64) とナノ秒 (nsec int32) で取得します。
    • これにより、Goのランタイムが直接、高精度な現在時刻を取得できるようになり、time パッケージのパフォーマンスが向上しました。
  6. テストコードの追加と更新:

    • 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.CTime 型を送信するように変更。
  • src/pkg/time/tick.go: NewTicker, Tick 関数が Duration 型を受け取るように変更され、Ticker.CTime 型を送信するように変更。
  • 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
}

この新しい構造体は、時間を「絶対的な瞬間」として secnsec で表現し、その「表示方法」を 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 構造体のフィールドにマッピングするロジックが強化されました。タイムゾーンの解析もより柔軟に行えるようになっています。

itoaatoi の導入は、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のメーリングリストなど)

参考にした情報源リンク