[インデックス 10740] ファイルの概要
このコミットは、Go言語のバージョン1(Go 1)リリースにおけるtime
パッケージの大幅な再設計に関する公式ドキュメントの更新を目的としています。具体的には、doc/go1.html
およびdoc/go1.tmpl
に新しいtime
パッケージのAPIとセマンティクスに関する詳細な説明を追加し、doc/progs/go1.go
にその使用例を盛り込んでいます。これにより、Go 1で導入された時間管理の新しいアプローチが開発者に明確に伝えられます。
コミット
commit 5fa18e10618d609ce2c272026e460c47ec864250
Author: Rob Pike <r@golang.org>
Date: Mon Dec 12 21:08:03 2011 -0800
doc/go1: time
R=rsc
CC=golang-dev
https://golang.org/cl/5477077
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5fa18e10618d609ce2c272026e460c47ec864250
元コミット内容
doc/go1: time
R=rsc
CC=golang-dev
https://golang.org/cl/5477077
変更の背景
Go言語は、2012年3月に最初の安定版であるGo 1をリリースしました。Go 1の目標は、言語と標準ライブラリの安定性を提供し、将来のバージョンとの互換性を保証することでした。この安定化プロセスの一環として、既存のAPIのレビューと改善が行われました。
time
パッケージは、Go 1以前に存在していましたが、そのAPIは使いにくく、直感的ではない部分がありました。特に、時間の「瞬間」と「期間」を区別する明確な型がなく、ナノ秒単位のint64
とポインタ型の*time.Time
が混在していました。これにより、コードの可読性や安全性が損なわれる可能性がありました。
このコミットは、Go 1のリリースに向けてtime
パッケージが大幅に再設計されたことを受けて、その変更点を公式ドキュメントに反映させるために作成されました。開発者が新しいAPIを理解し、既存のコードをスムーズに移行できるよう、詳細な説明と具体的なコード例が追加されています。
前提知識の解説
- Go 1リリース: Go言語の最初のメジャー安定版リリース。このリリース以降、Go言語の互換性保証が始まり、既存のコードが将来のGoバージョンで動作し続けることが約束されました。
- Go言語の型システム(値型とポインタ型): Go言語では、変数は値型(例:
int
,string
,struct
)またはポインタ型(例:*int
,*MyStruct
)として扱われます。値型はデータのコピーを渡し、ポインタ型はデータのメモリアドレスを渡します。time.Time
がポインタ型から値型に変更されたことは、その使用方法に大きな影響を与えます。 - Unixエポック: 1970年1月1日00:00:00 UTCを基準点とする時刻表現。多くのシステムで時刻の内部表現として使用されます。Goの旧
time
パッケージでは、time.Now()
がUnixエポックからのナノ秒数を返していました。 gofix
ツール: Go言語のツールチェーンに含まれるコマンドラインツール。Go言語のAPIが変更された際に、古いAPIを使用しているコードを新しいAPIに自動的に書き換える機能を提供します。これにより、大規模なコードベースの移行作業が容易になります。ただし、すべてのケースを完全に自動で処理できるわけではなく、手動での修正が必要な場合もあります。- Go言語のパッケージ: Go言語のコードはパッケージに分割され、関連する機能がまとめられています。
time
パッケージは、日付と時刻の操作に関連する機能を提供します。
技術的詳細
Go 1におけるtime
パッケージの再設計は、時間管理の概念をより明確にし、APIの使いやすさと堅牢性を向上させることを目的としていました。
旧APIの問題点
Go 1以前のtime
パッケージでは、時間の瞬間をint64
型のナノ秒数で表現したり、*time.Time
というポインタ型で表現したりしていました。これにより、以下のような問題がありました。
- 概念の混同: 時間の「瞬間」と「期間」が明確に区別されておらず、
int64
が両方の意味で使われることがありました。 - ポインタ型の煩雑さ:
*time.Time
は常にポインタとして扱われるため、値のコピーではなく参照渡しが基本となり、意図しない副作用やnilポインタの扱いに関する注意が必要でした。 - Unixエポックへの依存:
time.Now()
が常にUnixエポックからのナノ秒数を返すため、特定の時間帯やカレンダーシステムに依存しない汎用的な時間操作が困難でした。
新APIの導入
Go 1では、これらの問題を解決するために、以下の2つの基本的な型が導入されました。
time.Time
:- 時間の「瞬間」を表す値型です(ポインタの
*
がなくなりました)。 - ナノ秒の精度を持ちます。
- 古代から遠い未来までのあらゆる時間を表現できます。
- これにより、
time.Time
の値を関数に渡す際に、値がコピーされるため、元の値が変更される心配がなくなりました。
- 時間の「瞬間」を表す値型です(ポインタの
time.Duration
:- 時間の「期間」または「間隔」を表す型です。
- ナノ秒の精度を持ちます。
- 約±290年間の期間を表現できます。
time.Second
のような便利な事前定義された定数が提供されます。
新しいメソッドとセマンティクス
新しいtime.Time
とtime.Duration
型には、直感的な時間操作を可能にする多くのメソッドが追加されました。
Time.Add(d Duration) Time
:Time
にDuration
を加算し、新しいTime
を返します。Time.Sub(t Time) Duration
: 2つのTime
の差を計算し、Duration
を返します。Time.After(u Time) bool
:Time
が別のTime
より後であるかを判定します。Time.Before(u Time) bool
:Time
が別のTime
より前であるかを判定します。
最も重要なセマンティックな変更は、Unixエポックの関連性が限定されたことです。
time.Unix()
、Time.Unix()
、Time.UnixNano()
といったUnixという名前が明示的に含まれる関数やメソッドのみがUnixエポックを基準とします。time.Now()
は、もはやUnixエポックからの整数ナノ秒数を返しません。 代わりに、現在の瞬間を表すtime.Time
値を返します。これにより、時間操作がより抽象的で汎用的になりました。
標準パッケージへの波及
新しい型、メソッド、定数は、os
パッケージにおけるファイルタイムスタンプの表現など、時間を使用するすべての標準パッケージに伝播されました。これにより、Goエコシステム全体で一貫した時間管理が可能になりました。
gofix
による移行支援
Go 1への移行を容易にするため、gofix
ツールが更新され、古いtime
パッケージの多くの使用箇所を新しい型とメソッドに自動的に更新できるようになりました。しかし、いくつかの注意点があります。
1e9
のようなナノ秒を表すリテラル値は自動的に置き換えられません。これらは手動でtime.Second
のような定数に置き換える必要があります。- 型変更によって生じる一部の式は、
gofix
による書き換え後も手動での修正が必要になる場合があります。gofix
は正しい関数やメソッドを提案しますが、型が合わない場合や、さらなる分析が必要な場合があります。
コアとなるコードの変更箇所
このコミットでは、主に以下の3つのファイルが変更されています。
doc/go1.html
:- Go 1の変更点をまとめたHTMLドキュメント。
time
パッケージのセクションが大幅に拡張され、新しいtime.Time
とtime.Duration
の概念、主要なメソッド、Unixエポックの扱いの変更、gofix
による移行に関する説明が追加されました。structEquality
のコード例がコメントアウトから解除され、Go 1で構造体がマップのキーとして使用できるようになったことを示すコードが有効化されました。
doc/go1.tmpl
:doc/go1.html
を生成するためのテンプレートファイル。doc/go1.html
に追加されたtime
パッケージに関する説明のテンプレートコードが追加されました。
doc/progs/go1.go
:doc/go1.html
で使用されるコード例を含むGoプログラム。time
パッケージをインポートする行が追加されました。main
関数にtimePackage()
の呼び出しが追加されました。sleepUntil
関数が追加されました。これは、time.Time
、time.Duration
、time.Now()
、Time.Sub()
、time.Sleep()
の使用方法を示す具体的な例です。timePackage
関数が追加され、sleepUntil
を呼び出すことでtime
パッケージの新しいAPIの利用例を示しています。structEquality
関数内のコードがコメントアウトから解除され、構造体をマップのキーとして使用する例が有効化されました。
コアとなるコードの解説
doc/progs/go1.go
におけるtime
パッケージの例
このコミットで追加されたsleepUntil
関数は、新しいtime
パッケージのAPIを理解する上で非常に重要です。
// sleepUntil sleeps until the specified time. It returns immediately if it's too late.
func sleepUntil(wakeup time.Time) {
now := time.Now() // A Time.
if !wakeup.After(now) {
return
}
delta := wakeup.Sub(now) // A Duration.
log.Printf("Sleeping for %.3fs", delta.Seconds())
time.Sleep(delta)
}
func timePackage() {
sleepUntil(time.Now().Add(123 * time.Millisecond))
}
func sleepUntil(wakeup time.Time)
: この関数は、引数としてtime.Time
型のwakeup
(目覚める時刻)を受け取ります。これは、時間の「瞬間」を表す値型です。now := time.Now()
:time.Now()
は、現在の瞬間を表すtime.Time
値を返します。旧APIのようにint64
のナノ秒数を返すわけではありません。if !wakeup.After(now)
:Time.After()
メソッドは、wakeup
がnow
より後であるかを判定します。これにより、すでに指定時刻を過ぎている場合はすぐにリターンします。delta := wakeup.Sub(now)
:Time.Sub()
メソッドは、2つのtime.Time
値の差を計算し、time.Duration
型の値を返します。このdelta
は、スリープすべき「期間」を表します。log.Printf("Sleeping for %.3fs", delta.Seconds())
:Duration.Seconds()
メソッドは、time.Duration
を秒単位のfloat64
として返します。これにより、期間を人間が読める形式で出力できます。time.Sleep(delta)
:time.Sleep()
関数は、引数としてtime.Duration
を受け取り、その期間だけ現在のゴルーチンをスリープさせます。func timePackage()
: この関数は、sleepUntil
の使用例を示しています。time.Now().Add(123 * time.Millisecond)
は、現在の時刻に123ミリ秒の期間を加算した新しいtime.Time
値を生成しています。time.Millisecond
はtime.Duration
型の定数であり、期間の表現に役立ちます。
この例は、time.Time
が時間の瞬間を、time.Duration
が時間の期間をそれぞれ明確に表現し、それらの間で直感的な操作(加算、減算、比較)が可能になったことを示しています。
structEquality
の変更
doc/progs/go1.go
のstructEquality
関数内の変更は、time
パッケージとは直接関係ありませんが、Go 1で導入された別の重要な機能を示しています。
type Day struct {
long string
short string
}
Christmas := Day{"Christmas", "XMas"}
Thanksgiving := Day{"Thanksgiving", "Turkey"}
holiday := map[Day]bool{
Christmas: true,
Thanksgiving: true,
}
fmt.Printf("Christmas is a holiday: %t\n", holiday[Christmas])
このコードは、Go 1で構造体(struct)をマップのキーとして使用できるようになったことを示しています。Go 1以前は、マップのキーとして使用できるのは比較可能な型(プリミティブ型、ポインタ、インターフェース、配列、構造体、関数など)に限られており、構造体は比較可能ではありませんでした。Go 1では、すべてのフィールドが比較可能な構造体は比較可能となり、マップのキーとして使用できるようになりました。これにより、より複雑なデータ構造をマップのキーとして利用できるようになり、柔軟性が向上しました。
関連リンク
- Go言語公式ドキュメント: https://go.dev/doc/
- Go 1リリースノート: https://go.dev/doc/go1
time
パッケージのドキュメント: https://pkg.go.dev/time- Go言語の
gofix
ツールについて: https://go.dev/cmd/gofix/
参考にした情報源リンク
- Go 1 Release Notes - Time: https://go.dev/doc/go1#time
- Go 1 Release Notes - Map keys: https://go.dev/doc/go1#map_keys
- A Tour of Go - Time: https://go.dev/tour/moretypes/20 (これは現在のGoのツアーであり、Go 1当時のものではないが、
time
パッケージの基本的な使い方を理解するのに役立つ) - Go言語の
time
パッケージに関するブログ記事やチュートリアル(具体的なURLは検索結果によるため省略) - Go言語の
gofix
に関する情報(具体的なURLは検索結果によるため省略) - Go言語の構造体の比較可能性に関する情報(具体的なURLは検索結果によるため省略)