[インデックス 10347] ファイルの概要
このコミットは、Go言語の標準ライブラリにおいて、os.Time
関数の使用を廃止し、関連する依存関係の循環問題を解決するための変更です。具体的には、io/ioutil
パッケージとnet
パッケージからos.Time
への依存を取り除き、time
パッケージ内でのio/ioutil
の使用をbytes.Buffer
を用いたファイル読み込みに置き換えることで、依存関係の循環を解消しています。
コミット
commit a6c501e4b1c0bc21e85a51ef62af65c4f284fff1
Author: Anthony Martin <ality@pbrane.org>
Date: Fri Nov 11 14:40:41 2011 -0500
net, io/ioutil: remove use of os.Time
I had to replace the single use of io/ioutil
in the time package with a bytes.Buffer since
there would've been a dependency cycle.
There are no other uses of os.Time.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5372054
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/a6c501e4b1c0bc21e85a51ef62af65c4f284fff1
元コミット内容
このコミットの元の内容は、os.Time
関数の使用をnet
パッケージとio/ioutil
パッケージから削除することです。また、time
パッケージがio/ioutil
を使用している唯一の箇所をbytes.Buffer
に置き換えることで、time
パッケージとio/ioutil
間の依存関係の循環を解消しています。コミットメッセージは、os.Time
の他の使用箇所がないことを明記しています。
変更の背景
この変更の背景には、Go言語の標準ライブラリ設計における依存関係の整理と、より適切なAPIの選択があります。
-
os.Time
の廃止:os.Time
は、Goの初期バージョンに存在した関数で、システム時刻を秒とナノ秒で返すものでした。しかし、Goの設計思想として、特定の機能はより専門的なパッケージに集約されるべきという考え方があります。時刻に関する機能はtime
パッケージに集約されるべきであり、os
パッケージが時刻情報を提供する役割を持つのは適切ではないと判断されたと考えられます。これにより、time
パッケージが提供するより高レベルで柔軟な時刻API(例:time.Now()
,time.Nanoseconds()
,time.Seconds()
)への移行が促されました。 -
依存関係の循環 (Dependency Cycle): コミットメッセージで言及されている「dependency cycle」は、Goのパッケージ管理において非常に重要な問題です。Goでは、パッケージAがパッケージBをインポートし、同時にパッケージBがパッケージAをインポートするような循環参照は許されません。 このコミットでは、
time
パッケージがio/ioutil
をインポートし、同時にio/ioutil
がos
をインポートし、さらにos
がtime
パッケージの機能(os.Time
)に依存しているという状況が発生していた可能性があります。具体的には、time
パッケージがio/ioutil.ReadFile
を使ってタイムゾーン情報を読み込み、一方でio/ioutil
がos.Time
を使って一時ファイル名を生成していたため、time
->io/ioutil
->os
->time
という循環参照が生じていたと推測されます。 このような循環参照は、コンパイルエラーを引き起こすか、あるいはコードの理解と保守を極めて困難にします。このコミットは、time
パッケージがio/ioutil
に依存する部分を、より低レベルなos.Open
とbytes.Buffer
を使った直接的なファイル読み込みに置き換えることで、この循環を断ち切っています。 -
APIの洗練:
os.Time
のような低レベルな時刻取得APIは、より抽象化されたtime
パッケージの関数に置き換えられることで、コードの可読性と堅牢性が向上します。time.Nanoseconds()
やtime.Seconds()
は、それぞれナノ秒単位、秒単位のUnixエポックからの経過時間を返すため、os.Time
が返していた秒とナノ秒のタプルを扱うよりもシンプルです。
前提知識の解説
このコミットを理解するためには、以下のGo言語の基本的な概念と標準ライブラリの知識が必要です。
- Go言語のパッケージシステム: Goのコードはパッケージに分割され、
import
キーワードを使って他のパッケージの機能を利用します。Goのビルドシステムは、パッケージ間の依存関係を解決しますが、循環参照は許可されません。 os
パッケージ: オペレーティングシステムと対話するための機能(ファイル操作、プロセス管理、環境変数など)を提供します。time
パッケージ: 時刻と日付を扱うための機能(現在時刻の取得、時刻のフォーマット、時間間隔の計算など)を提供します。io/ioutil
パッケージ: I/O操作に関するユーティリティ関数を提供します。特にReadFile
は、ファイルの内容をバイトスライスとして一度に読み込む便利な関数です。net
パッケージ: ネットワークI/Oに関する機能(TCP/UDP接続、DNSルックアップなど)を提供します。bytes
パッケージ: バイトスライスを操作するための機能を提供します。bytes.Buffer
は、可変長のバイトバッファとして機能し、io.Reader
やio.Writer
インターフェースを実装しているため、I/O操作の中間バッファとして非常に便利です。os.Time()
(旧API): Goの初期バージョンに存在した関数で、func Time() (sec, nsec int64, mono int64)
のように、Unixエポックからの秒数とナノ秒数を返していました。このコミットの時点では既に非推奨または削除の対象となっていました。time.Nanoseconds()
/time.Seconds()
:time
パッケージが提供する関数で、それぞれUnixエポックからの経過時間をナノ秒単位、秒単位でint64
として返します。これらはos.Time()
の代替として使用されます。os.Open()
:os
パッケージの関数で、指定されたパスのファイルを読み取り用に開きます。ファイルディスクリプタ(*os.File
)を返します。io.Reader
インターフェース:Read(p []byte) (n int, err error)
メソッドを持つインターフェースで、データを読み込むことができる型が実装します。bytes.Buffer.ReadFrom(r io.Reader)
:bytes.Buffer
のメソッドで、io.Reader
からEOFに達するまでデータを読み込み、バッファに追加します。
技術的詳細
このコミットの技術的詳細は、Go言語のパッケージ設計原則と、特定のAPIの進化に焦点を当てています。
os.Time
の廃止と代替
os.Time
は、Goの初期段階でシステム時刻を取得するために使用されていましたが、その役割はより専門的なtime
パッケージに移行されました。os.Time
は、秒とナノ秒を別々に返す形式でしたが、time
パッケージのtime.Nanoseconds()
やtime.Seconds()
は、単一のint64
値としてナノ秒または秒単位のUnixエポックからの経過時間を返します。これにより、時刻の扱いがより統一され、簡潔になりました。
-
io/ioutil/tempfile.go
での変更:- 変更前:
sec, nsec, _ := os.Time()
を使用して時刻を取得し、uint32(sec*1e9 + nsec + int64(os.Getpid()))
で乱数のシードを生成していました。 - 変更後:
uint32(time.Nanoseconds() + int64(os.Getpid()))
を使用しています。time.Nanoseconds()
は直接ナノ秒単位のint64
を返すため、秒とナノ秒を結合する手間が省かれ、コードが簡潔になっています。
- 変更前:
-
net/hosts.go
での変更:- 変更前:
now, _, _ := os.Time()
を使用して現在時刻を取得し、hosts.time, _, _ = os.Time()
でキャッシュの更新時刻を設定していました。 - 変更後:
now := time.Seconds()
とhosts.time = time.Seconds()
を使用しています。ここでは、キャッシュの有効期限を秒単位で管理しているため、time.Seconds()
が適切です。
- 変更前:
依存関係の循環の解消
最も重要な変更は、src/pkg/time/zoneinfo_unix.go
におけるio/ioutil.ReadFile
の使用の置き換えです。
-
問題の構造:
time
パッケージはタイムゾーン情報をファイルから読み込むためにio/ioutil.ReadFile
を使用。io/ioutil
パッケージは一時ファイル名を生成するためにos.Time
を使用。os.Time
は、time
パッケージの内部実装に依存している(または、os
パッケージがtime
パッケージをインポートしている)。- これにより、
time
->io/ioutil
->os
->time
という循環参照が発生し、Goのビルドシステムがこれを解決できませんでした。
-
解決策:
io/ioutil.ReadFile(name)
の代わりに、より低レベルなファイル操作とbytes.Buffer
を組み合わせることで、time
パッケージからio/ioutil
への直接的な依存を解消しました。- 具体的には、
os.Open(name)
でファイルを開き、そのファイルディスクリプタ(*os.File
)をbytes.Buffer
のReadFrom
メソッドに渡して内容を読み込んでいます。 bytes.Buffer
はio.Reader
インターフェースを実装しているため、f.ReadFrom(f)
のように*os.File
から直接データを読み込むことができます。これにより、io/ioutil
を介さずにファイルの内容をメモリにロードすることが可能になります。
この変更により、time
パッケージはos
パッケージとbytes
パッケージにのみ依存するようになり、io/ioutil
への依存がなくなりました。結果として、循環参照が解消され、パッケージ構造がより健全になりました。
コアとなるコードの変更箇所
src/pkg/io/ioutil/tempfile.go
--- a/src/pkg/io/ioutil/tempfile.go
+++ b/src/pkg/io/ioutil/tempfile.go
@@ -8,6 +8,7 @@ import (
"os"
"path/filepath"
"strconv"
+ "time"
)
// Random number state, accessed without lock; racy but harmless.
@@ -17,8 +18,7 @@ var rand uint32
func reseed() uint32 {
- sec, nsec, _ := os.Time()
- return uint32(sec*1e9 + nsec + int64(os.Getpid()))
+ return uint32(time.Nanoseconds() + int64(os.Getpid()))
}
func nextSuffix() string {
src/pkg/net/hosts.go
--- a/src/pkg/net/hosts.go
+++ b/src/pkg/net/hosts.go
@@ -7,8 +7,8 @@
package net
import (
- "os"
"sync"
+ "time"
)
const cacheMaxAge = int64(300) // 5 minutes.
@@ -26,7 +26,7 @@ var hosts struct {
}
func readHosts() {
- now, _, _ := os.Time()
+ now := time.Seconds()
hp := hostsPath
if len(hosts.byName) == 0 || hosts.time+cacheMaxAge <= now || hosts.path != hp {
hs := make(map[string][]string)
@@ -51,7 +51,7 @@ func readHosts() {
}
}
// Update the data cache.
- hosts.time, _, _ = os.Time()
+ hosts.time = time.Seconds()
hosts.path = hp
hosts.byName = hs
hosts.byAddr = is
src/pkg/time/zoneinfo_unix.go
--- a/src/pkg/time/zoneinfo_unix.go
+++ b/src/pkg/time/zoneinfo_unix.go
@@ -12,7 +12,7 @@
package time
import (
- "io/ioutil"
+ "bytes"
"os"
)
@@ -180,11 +180,17 @@ func parseinfo(bytes []byte) (zt []zonetime, ok bool) {
}
func readinfofile(name string) ([]zonetime, bool) {
- buf, err := ioutil.ReadFile(name)
+ var b bytes.Buffer
+
+ f, err := os.Open(name)
if err != nil {
return nil, false
}
- return parseinfo(buf)
+ defer f.Close()
+ if _, err := b.ReadFrom(f); err != nil {
+ return nil, false
+ }
+ return parseinfo(b.Bytes())
}
func setupTestingZone() {
コアとなるコードの解説
src/pkg/io/ioutil/tempfile.go
- 変更前:
reseed
関数内でos.Time()
を呼び出し、その戻り値である秒とナノ秒を使って乱数のシードを生成していました。 - 変更後:
os.Time()
の代わりにtime.Nanoseconds()
を呼び出すように変更されました。time.Nanoseconds()
は直接ナノ秒単位のint64
を返すため、sec*1e9 + nsec
のような計算が不要になり、コードがより簡潔になりました。os
パッケージのインポートが削除され、time
パッケージが新しくインポートされています。
src/pkg/net/hosts.go
- 変更前:
readHosts
関数内でos.Time()
を呼び出し、現在時刻の取得とキャッシュの更新時刻の設定を行っていました。 - 変更後:
os.Time()
の代わりにtime.Seconds()
を呼び出すように変更されました。time.Seconds()
は秒単位のint64
を返すため、キャッシュの有効期限(cacheMaxAge
が秒単位)との比較に適しています。ここでもos
パッケージのインポートが削除され、time
パッケージが新しくインポートされています。
src/pkg/time/zoneinfo_unix.go
- 変更前:
readinfofile
関数内でio/ioutil.ReadFile(name)
を呼び出し、指定されたファイルの内容を一度に読み込んでいました。 - 変更後:
io/ioutil
パッケージのインポートが削除され、代わりにbytes
パッケージがインポートされました。ioutil.ReadFile
の代わりに、bytes.Buffer
とos.Open
を組み合わせた手動でのファイル読み込みロジックが導入されました。var b bytes.Buffer
で新しいバイトバッファを初期化します。f, err := os.Open(name)
でファイルを読み取り用に開きます。エラーハンドリングも適切に行われています。defer f.Close()
で関数終了時にファイルが確実に閉じられるようにします。if _, err := b.ReadFrom(f); err != nil
で、開いたファイルf
からbytes.Buffer
b
へデータを読み込みます。ReadFrom
メソッドはio.Reader
インターフェースを引数にとり、ファイルの内容を効率的にバッファにコピーします。- 最後に
parseinfo(b.Bytes())
を呼び出し、バッファの内容をバイトスライスとして渡しています。
このtime/zoneinfo_unix.go
の変更が、time
パッケージとio/ioutil
パッケージ間の依存関係の循環を解消する上で最も重要な部分です。io/ioutil.ReadFile
への依存をなくすことで、time
パッケージはio/ioutil
をインポートする必要がなくなり、循環参照が断ち切られました。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/doc/
- Go言語のパッケージドキュメント:
os
パッケージ: https://pkg.go.dev/ostime
パッケージ: https://pkg.go.dev/timeio/ioutil
パッケージ (Go 1.16で非推奨、Go 1.19で削除): https://pkg.go.dev/io/ioutil (現在のバージョンでは非推奨または削除されているため、当時の状況を理解するために参照)bytes
パッケージ: https://pkg.go.dev/bytesnet
パッケージ: https://pkg.go.dev/net
- Goの変更リスト (CL): https://golang.org/cl/5372054 (コミットメッセージに記載されているCLへのリンク)
参考にした情報源リンク
- Go言語の公式リポジトリのコミット履歴
- Go言語のパッケージドキュメント (特に
os
,time
,io/ioutil
,bytes
,net
パッケージの当時のバージョンに関する情報) - Go言語の設計に関する議論やメーリングリストのアーカイブ (
os.Time
の廃止や依存関係の循環に関する議論を特定するため) - Go言語のバージョンごとの変更履歴 (特にGo 1.0リリース前の開発段階の変更点)
[インデックス 10347] ファイルの概要
このコミットは、Go言語の標準ライブラリにおいて、os.Time
関数の使用を廃止し、関連する依存関係の循環問題を解決するための変更です。具体的には、io/ioutil
パッケージとnet
パッケージからos.Time
への依存を取り除き、time
パッケージ内でのio/ioutil
の使用をbytes.Buffer
を用いたファイル読み込みに置き換えることで、依存関係の循環を解消しています。
コミット
commit a6c501e4b1c0bc21e85a51ef62af65c4f284fff1
Author: Anthony Martin <ality@pbrane.org>
Date: Fri Nov 11 14:40:41 2011 -0500
net, io/ioutil: remove use of os.Time
I had to replace the single use of io/ioutil
in the time package with a bytes.Buffer since
there would've been a dependency cycle.
There are no other uses of os.Time.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5372054
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/a6c501e4b1c0bc21e85a51ef62af65c4f284fff1
元コミット内容
このコミットの元の内容は、os.Time
関数の使用をnet
パッケージとio/ioutil
パッケージから削除することです。また、time
パッケージがio/ioutil
を使用している唯一の箇所をbytes.Buffer
に置き換えることで、time
パッケージとio/ioutil
間の依存関係の循環を解消しています。コミットメッセージは、os.Time
の他の使用箇所がないことを明記しています。
変更の背景
この変更の背景には、Go言語の標準ライブラリ設計における依存関係の整理と、より適切なAPIの選択があります。
-
os.Time
の廃止:os.Time
は、Goの初期バージョンに存在した関数で、システム時刻を秒とナノ秒で返すものでした。しかし、Goの設計思想として、特定の機能はより専門的なパッケージに集約されるべきという考え方があります。時刻に関する機能はtime
パッケージに集約されるべきであり、os
パッケージが時刻情報を提供する役割を持つのは適切ではないと判断されたと考えられます。これにより、time
パッケージが提供するより高レベルで柔軟な時刻API(例:time.Now()
,time.Nanoseconds()
,time.Seconds()
)への移行が促されました。 -
依存関係の循環 (Dependency Cycle): コミットメッセージで言及されている「dependency cycle」は、Goのパッケージ管理において非常に重要な問題です。Goでは、パッケージAがパッケージBをインポートし、同時にパッケージBがパッケージAをインポートするような循環参照は許されません。 このコミットでは、
time
パッケージがio/ioutil
をインポートし、同時にio/ioutil
がos
をインポートし、さらにos
がtime
パッケージの機能(os.Time
)に依存しているという状況が発生していた可能性があります。具体的には、time
パッケージがio/ioutil.ReadFile
を使ってタイムゾーン情報を読み込み、一方でio/ioutil
がos.Time
を使って一時ファイル名を生成していたため、time
->io/ioutil
->os
->time
という循環参照が生じていたと推測されます。 このような循環参照は、コンパイルエラーを引き起こすか、あるいはコードの理解と保守を極めて困難にします。このコミットは、time
パッケージがio/ioutil
に依存する部分を、より低レベルなos.Open
とbytes.Buffer
を使った直接的なファイル読み込みに置き換えることで、この循環を断ち切っています。 -
APIの洗練:
os.Time
のような低レベルな時刻取得APIは、より抽象化されたtime
パッケージの関数に置き換えられることで、コードの可読性と堅牢性が向上します。time.Nanoseconds()
やtime.Seconds()
は、それぞれナノ秒単位、秒単位のUnixエポックからの経過時間を返すため、os.Time
が返していた秒とナノ秒のタプルを扱うよりもシンプルです。
前提知識の解説
このコミットを理解するためには、以下のGo言語の基本的な概念と標準ライブラリの知識が必要です。
- Go言語のパッケージシステム: Goのコードはパッケージに分割され、
import
キーワードを使って他のパッケージの機能を利用します。Goのビルドシステムは、パッケージ間の依存関係を解決しますが、循環参照は許可されません。 os
パッケージ: オペレーティングシステムと対話するための機能(ファイル操作、プロセス管理、環境変数など)を提供します。time
パッケージ: 時刻と日付を扱うための機能(現在時刻の取得、時刻のフォーマット、時間間隔の計算など)を提供します。io/ioutil
パッケージ: I/O操作に関するユーティリティ関数を提供します。特にReadFile
は、ファイルの内容をバイトスライスとして一度に読み込む便利な関数です。net
パッケージ: ネットワークI/Oに関する機能(TCP/UDP接続、DNSルックアップなど)を提供します。bytes
パッケージ: バイトスライスを操作するための機能を提供します。bytes.Buffer
は、可変長のバイトバッファとして機能し、io.Reader
やio.Writer
インターフェースを実装しているため、I/O操作の中間バッファとして非常に便利です。os.Time()
(旧API): Goの初期バージョンに存在した関数で、func Time() (sec, nsec int64, mono int64)
のように、Unixエポックからの秒数とナノ秒数を返していました。このコミットの時点では既に非推奨または削除の対象となっていました。time.Nanoseconds()
/time.Seconds()
:time
パッケージが提供する関数で、それぞれUnixエポックからの経過時間をナノ秒単位、秒単位でint64
として返します。これらはos.Time()
の代替として使用されます。os.Open()
:os
パッケージの関数で、指定されたパスのファイルを読み取り用に開きます。ファイルディスクリプタ(*os.File
)を返します。io.Reader
インターフェース:Read(p []byte) (n int, err error)
メソッドを持つインターフェースで、データを読み込むことができる型が実装します。bytes.Buffer.ReadFrom(r io.Reader)
:bytes.Buffer
のメソッドで、io.Reader
からEOFに達するまでデータを読み込み、バッファに追加します。
技術的詳細
このコミットの技術的詳細は、Go言語のパッケージ設計原則と、特定のAPIの進化に焦点を当てています。
os.Time
の廃止と代替
os.Time
は、Goの初期段階でシステム時刻を取得するために使用されていましたが、その役割はより専門的なtime
パッケージに移行されました。os.Time
は、秒とナノ秒を別々に返す形式でしたが、time
パッケージのtime.Nanoseconds()
やtime.Seconds()
は、単一のint64
値としてナノ秒または秒単位のUnixエポックからの経過時間を返します。これにより、時刻の扱いがより統一され、簡潔になりました。
-
io/ioutil/tempfile.go
での変更:- 変更前:
sec, nsec, _ := os.Time()
を使用して時刻を取得し、uint32(sec*1e9 + nsec + int64(os.Getpid()))
で乱数のシードを生成していました。 - 変更後:
uint32(time.Nanoseconds() + int64(os.Getpid()))
を使用しています。time.Nanoseconds()
は直接ナノ秒単位のint64
を返すため、秒とナノ秒を結合する手間が省かれ、コードが簡潔になっています。
- 変更前:
-
net/hosts.go
での変更:- 変更前:
now, _, _ := os.Time()
を使用して現在時刻を取得し、hosts.time, _, _ = os.Time()
でキャッシュの更新時刻を設定していました。 - 変更後:
now := time.Seconds()
とhosts.time = time.Seconds()
を使用しています。ここでは、キャッシュの有効期限を秒単位で管理しているため、time.Seconds()
が適切です。
- 変更前:
依存関係の循環の解消
最も重要な変更は、src/pkg/time/zoneinfo_unix.go
におけるio/ioutil.ReadFile
の使用の置き換えです。
-
問題の構造:
time
パッケージはタイムゾーン情報をファイルから読み込むためにio/ioutil.ReadFile
を使用。io/ioutil
パッケージは一時ファイル名を生成するためにos.Time
を使用。os.Time
は、time
パッケージの内部実装に依存している(または、os
パッケージがtime
パッケージをインポートしている)。- これにより、
time
->io/ioutil
->os
->time
という循環参照が発生し、Goのビルドシステムがこれを解決できませんでした。
-
解決策:
io/ioutil.ReadFile(name)
の代わりに、より低レベルなファイル操作とbytes.Buffer
を組み合わせることで、time
パッケージからio/ioutil
への直接的な依存を解消しました。- 具体的には、
os.Open(name)
でファイルを開き、そのファイルディスクリプタ(*os.File
)をbytes.Buffer
のReadFrom
メソッドに渡して内容を読み込んでいます。 bytes.Buffer
はio.Reader
インターフェースを実装しているため、f.ReadFrom(f)
のように*os.File
から直接データを読み込むことができます。これにより、io/ioutil
を介さずにファイルの内容をメモリにロードすることが可能になります。
この変更により、time
パッケージはos
パッケージとbytes
パッケージにのみ依存するようになり、io/ioutil
への依存がなくなりました。結果として、循環参照が解消され、パッケージ構造がより健全になりました。
コアとなるコードの変更箇所
src/pkg/io/ioutil/tempfile.go
--- a/src/pkg/io/ioutil/tempfile.go
+++ b/src/pkg/io/ioutil/tempfile.go
@@ -8,6 +8,7 @@ import (
"os"
"path/filepath"
"strconv"
+ "time"
)
// Random number state, accessed without lock; racy but harmless.
@@ -17,8 +18,7 @@ var rand uint32
func reseed() uint32 {
- sec, nsec, _ := os.Time()
- return uint32(sec*1e9 + nsec + int64(os.Getpid()))
+ return uint32(time.Nanoseconds() + int64(os.Getpid()))
}
func nextSuffix() string {
src/pkg/net/hosts.go
--- a/src/pkg/net/hosts.go
+++ b/src/pkg/net/hosts.go
@@ -7,8 +7,8 @@
package net
import (
- "os"
"sync"
+ "time"
)
const cacheMaxAge = int64(300) // 5 minutes.
@@ -26,7 +26,7 @@ var hosts struct {
}
func readHosts() {
- now, _, _ := os.Time()
+ now := time.Seconds()
hp := hostsPath
if len(hosts.byName) == 0 || hosts.time+cacheMaxAge <= now || hosts.path != hp {
hs := make(map[string][]string)
@@ -51,7 +51,7 @@ func readHosts() {
}
}
// Update the data cache.
- hosts.time, _, _ = os.Time()
+ hosts.time = time.Seconds()
hosts.path = hp
hosts.byName = hs
hosts.byAddr = is
src/pkg/time/zoneinfo_unix.go
--- a/src/pkg/time/zoneinfo_unix.go
+++ b/src/pkg/time/zoneinfo_unix.go
@@ -12,7 +12,7 @@
package time
import (
- "io/ioutil"
+ "bytes"
"os"
)
@@ -180,11 +180,17 @@ func parseinfo(bytes []byte) (zt []zonetime, ok bool) {
}
func readinfofile(name string) ([]zonetime, bool) {
- buf, err := ioutil.ReadFile(name)
+ var b bytes.Buffer
+
+ f, err := os.Open(name)
if err != nil {
return nil, false
}
- return parseinfo(buf)
+ defer f.Close()
+ if _, err := b.ReadFrom(f); err != nil {
+ return nil, false
+ }
+ return parseinfo(b.Bytes())
}
func setupTestingZone() {
コアとなるコードの解説
src/pkg/io/ioutil/tempfile.go
- 変更前:
reseed
関数内でos.Time()
を呼び出し、その戻り値である秒とナノ秒を使って乱数のシードを生成していました。 - 変更後:
os.Time()
の代わりにtime.Nanoseconds()
を呼び出すように変更されました。time.Nanoseconds()
は直接ナノ秒単位のint64
を返すため、sec*1e9 + nsec
のような計算が不要になり、コードがより簡潔になりました。os
パッケージのインポートが削除され、time
パッケージが新しくインポートされています。
src/pkg/net/hosts.go
- 変更前:
readHosts
関数内でos.Time()
を呼び出し、現在時刻の取得とキャッシュの更新時刻の設定を行っていました。 - 変更後:
os.Time()
の代わりにtime.Seconds()
を呼び出すように変更されました。time.Seconds()
は秒単位のint64
を返すため、キャッシュの有効期限(cacheMaxAge
が秒単位)との比較に適しています。ここでもos
パッケージのインポートが削除され、time
パッケージが新しくインポートされています。
src/pkg/time/zoneinfo_unix.go
- 変更前:
readinfofile
関数内でio/ioutil.ReadFile(name)
を呼び出し、指定されたファイルの内容を一度に読み込んでいました。 - 変更後:
io/ioutil
パッケージのインポートが削除され、代わりにbytes
パッケージがインポートされました。ioutil.ReadFile
の代わりに、bytes.Buffer
とos.Open
を組み合わせた手動でのファイル読み込みロジックが導入されました。var b bytes.Buffer
で新しいバイトバッファを初期化します。f, err := os.Open(name)
でファイルを読み取り用に開きます。エラーハンドリングも適切に行われています。defer f.Close()
で関数終了時にファイルが確実に閉じられるようにします。if _, err := b.ReadFrom(f); err != nil
で、開いたファイルf
からbytes.Buffer
b
へデータを読み込みます。ReadFrom
メソッドはio.Reader
インターフェースを引数にとり、ファイルの内容を効率的にバッファにコピーします。- 最後に
parseinfo(b.Bytes())
を呼び出し、バッファの内容をバイトスライスとして渡しています。
このtime/zoneinfo_unix.go
の変更が、time
パッケージとio/ioutil
パッケージ間の依存関係の循環を解消する上で最も重要な部分です。io/ioutil.ReadFile
への依存をなくすことで、time
パッケージはio/ioutil
をインポートする必要がなくなり、循環参照が断ち切られました。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/doc/
- Go言語のパッケージドキュメント:
os
パッケージ: https://pkg.go.dev/ostime
パッケージ: https://pkg.go.dev/timeio/ioutil
パッケージ (Go 1.16で非推奨、Go 1.19で削除): https://pkg.go.dev/io/ioutil (現在のバージョンでは非推奨または削除されているため、当時の状況を理解するために参照)bytes
パッケージ: https://pkg.go.dev/bytesnet
パッケージ: https://pkg.go.dev/net
- Goの変更リスト (CL): https://golang.org/cl/5372054 (コミットメッセージに記載されているCLへのリンク)
参考にした情報源リンク
- Go言語の公式リポジトリのコミット履歴
- Go言語のパッケージドキュメント (特に
os
,time
,io/ioutil
,bytes
,net
パッケージの当時のバージョンに関する情報) - Go言語の設計に関する議論やメーリングリストのアーカイブ (
os.Time
の廃止や依存関係の循環に関する議論を特定するため) - Go言語のバージョンごとの変更履歴 (特にGo 1.0リリース前の開発段階の変更点)
- Web検索: "Go os.Time deprecated 2011" (
os.Time
の廃止時期とtime
パッケージへの移行に関する情報確認のため)