[インデックス 12041] ファイルの概要
このコミットは、Go言語の time
パッケージにおいて、システム上にタイムゾーン情報ファイル(zoneinfo)が見つからない場合に、Goディストリビューションに含まれるzoneinfoデータを使用するようにフォールバックメカニズムを導入するものです。これにより、特にWindowsやPlan 9のような非Unix系システムでのタイムゾーン処理の堅牢性が向上します。
コミット
commit ad17a9c0a9e758a584f3d5992bc75fbde9512122
Author: Russ Cox <rsc@golang.org>
Date: Sat Feb 18 21:02:41 2012 -0500
time: use Go distribution zoneinfo if system copy not found
Fixes #2964.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5656101
---
src/pkg/time/zoneinfo.go | 22 ++++-
src/pkg/time/zoneinfo_plan9.go | 24 ++---
src/pkg/time/zoneinfo_read.go | 203 +++++++++++++++++++++++++++++++++++++++
src/pkg/time/zoneinfo_unix.go | 199 ++------------------------------------\
src/pkg/time/zoneinfo_windows.go | 5 +-\n 5 files changed, 243 insertions(+), 210 deletions(-)
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ad17a9c0a9e758a584f3d5992bc75fbde9512122
元コミット内容
time: use Go distribution zoneinfo if system copy not found
Fixes #2964.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5656101
変更の背景
この変更は、Goの time
パッケージがタイムゾーン情報を解決する際に直面していた問題に対処するために行われました。Goの time
パッケージは、タイムゾーンの計算にIANA Time Zone Database(tzdata)を利用します。しかし、このデータベースのファイル(zoneinfoファイル)は、オペレーティングシステムによって保存場所が異なります。特に、WindowsやPlan 9のようなUnix系ではないシステムでは、これらのファイルが標準的なUnixのパス(例: /usr/share/zoneinfo
)に存在しないことが一般的でした。
このため、Goアプリケーションがこれらのシステムで実行された際に、タイムゾーン情報を正しくロードできず、エラーが発生したり、UTC(協定世界時)にフォールバックしたりする問題がありました。Issue #2964は、この問題を具体的に指摘し、Goディストリビューション自体にzoneinfoファイルを含め、システムにファイルが見つからない場合にそれらを使用するメカニズムを導入することを提案していました。このコミットは、その提案を実装し、Goアプリケーションのタイムゾーン処理の移植性と信頼性を向上させることを目的としています。
前提知識の解説
IANA Time Zone Database (tzdata)
IANA Time Zone Database(旧称 tzdata、zoneinfo database)は、世界のタイムゾーンと夏時間(DST)のルールに関する情報を集約した公開データベースです。このデータベースは、タイムゾーンの歴史的な変更、将来の変更、および夏時間の開始・終了日など、複雑なタイムゾーン情報を網羅しています。多くのオペレーティングシステムやプログラミング言語が、正確な日時計算のためにこのデータベースを利用しています。
Zoneinfoファイル
IANA Time Zone Databaseのデータは、通常、バイナリ形式の「zoneinfoファイル」としてシステムに配布されます。これらのファイルは、特定のタイムゾーン(例: America/New_York
)に関するすべてのタイムゾーンルールとオフセット情報を含んでいます。Goの time
パッケージは、これらのファイルを読み込んでタイムゾーン情報を解析し、日時計算に利用します。
$GOROOT
$GOROOT
は、Goのインストールディレクトリを指す環境変数です。Goのコンパイラ、標準ライブラリ、ツールなどがこのディレクトリに格納されています。このコミットでは、システムにzoneinfoファイルが見つからない場合のフォールバックとして、$GOROOT
内にGoディストリビューションが提供するzoneinfoファイルを配置し、そこから読み込むように変更されました。
syscall.Getenv
syscall.Getenv
は、Go言語の syscall
パッケージに含まれる関数で、指定された環境変数の値を取得するために使用されます。このコミットでは、ZONEINFO
環境変数の値を読み取るために使用されています。
技術的詳細
このコミットの主要な技術的変更点は、Goの time
パッケージがタイムゾーン情報をロードする際の探索パスを拡張したことです。
-
ZONEINFO
環境変数の優先:LoadLocation
関数は、まずZONEINFO
環境変数が設定されているかどうかを確認します。もし設定されていれば、そのパスをタイムゾーンファイルの検索パスとして最優先で使用します。これにより、ユーザーはカスタムのzoneinfoディレクトリを指定できるようになります。 -
システム標準パスの探索: 従来のUnix系システムにおける標準的なzoneinfoパス(例:
/usr/share/zoneinfo
)の探索は引き続き行われます。 -
Goディストリビューション内のzoneinfoへのフォールバック: 最も重要な変更は、システム標準パスでzoneinfoファイルが見つからない場合に、Goディストリビューション自体に含まれるzoneinfoファイル(
$GOROOT/lib/time/zoneinfo/
)を探索するようになった点です。これにより、特にWindowsやPlan 9のような、システムにzoneinfoファイルが標準で含まれていない環境でも、Goアプリケーションがタイムゾーン情報を正しくロードできるようになります。 -
zoneinfo_read.go
の導入: zoneinfoファイルのバイナリ形式を解析するための共通ロジックが、新しく導入されたsrc/pkg/time/zoneinfo_read.go
ファイルに集約されました。これにより、各OS固有のファイル(zoneinfo_unix.go
,zoneinfo_plan9.go
,zoneinfo_windows.go
)から重複する解析ロジックが削除され、コードの保守性が向上しました。 -
OS固有の変更:
zoneinfo_unix.go
から、zoneinfoファイルの読み込みと解析に関する共通ロジックが削除され、zoneinfo_read.go
に移行しました。また、zoneDirs
に$GOROOT/lib/time/zoneinfo/
が追加されました。zoneinfo_plan9.go
では、loadZoneData
とloadZoneFile
の関数名がloadZoneDataPlan9
とloadZoneFilePlan9
に変更され、zoneinfo_read.go
の共通関数と区別されました。また、initLocal
およびloadLocation
で$GOROOT
内のzoneinfoを参照するようになりました。zoneinfo_windows.go
では、loadLocation
関数が$GOROOT
内のzoneinfoファイルを読み込むように実装されました。
これらの変更により、Goの time
パッケージは、より多くの環境でタイムゾーン処理を安定して行えるようになりました。
コアとなるコードの変更箇所
このコミットでは、主に以下のファイルが変更されています。
src/pkg/time/zoneinfo.go
:syscall
パッケージのインポートが追加されました。ZONEINFO
環境変数を読み取るためのzoneinfo
グローバル変数が追加されました。LoadLocation
関数が変更され、ZONEINFO
環境変数で指定されたパス、および$GOROOT/lib/time/zoneinfo
を探索するロジックが追加されました。
src/pkg/time/zoneinfo_plan9.go
:loadZoneData
とloadZoneFile
の関数名がloadZoneDataPlan9
とloadZoneFilePlan9
に変更されました。initTestingZone
とinitLocal
、loadLocation
関数が$GOROOT
内のzoneinfoファイルを優先的に参照するように変更されました。
src/pkg/time/zoneinfo_read.go
(新規ファイル):- zoneinfoファイルのバイナリ形式を解析するための共通ロジック(
data
構造体、read
,big4
,byte
,byteString
メソッド、loadZoneData
関数)が実装されました。 loadZoneFile
関数もこのファイルに移動されました。
- zoneinfoファイルのバイナリ形式を解析するための共通ロジック(
src/pkg/time/zoneinfo_unix.go
:zoneinfo_read.go
に移動された共通ロジック(data
構造体とそのメソッド、loadZoneData
関数)が削除されました。zoneDirs
スライスに$GOROOT/lib/time/zoneinfo/
が追加され、Unix系システムでもGoディストリビューションのzoneinfoが探索されるようになりました。
src/pkg/time/zoneinfo_windows.go
:loadLocation
関数が実装され、$GOROOT
内のzoneinfoファイルを読み込むようになりました。
コアとなるコードの解説
src/pkg/time/zoneinfo.go
// ...
import (
"sync"
"syscall" // 追加
)
// ...
var zoneinfo, _ = syscall.Getenv("ZONEINFO") // ZONEINFO環境変数を読み込む
// LoadLocation returns the Location with the given name.
// ...
func LoadLocation(name string) (*Location, error) {
if name == "" || name == "UTC" {
return UTC, nil
}
if name == "Local" {
return Local, nil
}
if zoneinfo != "" { // ZONEINFO環境変数が設定されている場合
if z, err := loadZoneFile(zoneinfo + "/" + name); err == nil { // そのパスを試す
z.name = name
return z, nil
}
}
return loadLocation(name) // OS固有のロードロジックを呼び出す
}
LoadLocation
関数は、タイムゾーン名を元に Location
オブジェクトをロードするGoの主要なAPIです。この変更により、まず ZONEINFO
環境変数が設定されているかを確認し、設定されていればそのパスを優先的に探索します。これにより、ユーザーはシステムに依存しないカスタムのタイムゾーンデータパスを指定できるようになります。
src/pkg/time/zoneinfo_read.go
(新規ファイル)
このファイルは、IANA zoneinfoファイルのバイナリ形式を解析するための汎用的なロジックを含んでいます。
// Parse "zoneinfo" time zone file.
// This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others.
// See tzfile(5), http://en.wikipedia.org/wiki/Zoneinfo,
// and ftp://munnari.oz.au/pub/oldtz/
package time
import "errors"
// Simple I/O interface to binary blob of data.
type data struct {
p []byte
error bool
}
// read, big4, byte, byteString などのヘルパーメソッドが定義されている
var badData = errors.New("malformed time zone information")
func loadZoneData(bytes []byte) (l *Location, err error) {
// zoneinfoファイルのバイナリデータを解析し、Location構造体を構築するロジック
// マジックナンバー "TZif" のチェック、ヘッダー情報の読み込み、
// タイムゾーン遷移情報、ゾーン情報、略語などの解析を行う
}
func loadZoneFile(name string) (l *Location, err error) {
buf, err := readFile(name) // ファイルを読み込む
if err != nil {
return
}
return loadZoneData(buf) // 読み込んだデータを解析する
}
loadZoneData
関数は、zoneinfoファイルのバイナリコンテンツをバイトスライスとして受け取り、それをGoの time.Location
構造体にパースします。この関数は、ファイルのヘッダー、タイムゾーン遷移情報、タイムゾーンのオフセットと夏時間フラグ、タイムゾーン名の略語など、zoneinfoファイルの複雑な構造を解釈します。loadZoneFile
は、指定されたパスからファイルを読み込み、その内容を loadZoneData
に渡して解析を行います。この共通化により、各OS固有のファイルから重複する解析ロジックが排除されました。
src/pkg/time/zoneinfo_unix.go
// ...
import (
"errors"
"runtime" // 追加
"syscall"
)
// ...
var zoneDirs = []string{
"/usr/share/zoneinfo/",
"/usr/share/lib/zoneinfo/",
"/usr/lib/locale/TZ/",
runtime.GOROOT() + "/lib/time/zoneinfo/", // 追加
}
func initLocal() {
// ...
}
func loadLocation(name string) (*Location, error) {
// zoneDirsを順に探索し、zoneinfoファイルをロードする
for _, zoneDir := range zoneDirs {
if z, err := loadZoneFile(zoneDir + name); err == nil {
z.name = name
return z, nil
}
}
return nil, errors.New("unknown time zone " + name)
}
zoneDirs
スライスに $GOROOT/lib/time/zoneinfo/
が追加されたことで、Unix系システムでも、標準的なシステムパスにzoneinfoファイルが見つからない場合に、Goディストリビューションに含まれるファイルが探索されるようになりました。これにより、Goのインストールだけでタイムゾーン処理が完結するようになり、外部のシステム設定に依存する度合いが低減されます。
src/pkg/time/zoneinfo_windows.go
// ...
func loadLocation(name string) (*Location, error) {
// Windowsでは、システムレジストリからタイムゾーン情報を取得するロジックがあるが、
// このコミットでは、GOROOT内のzoneinfoファイルも探索するようになった。
if z, err := loadZoneFile(runtime.GOROOT() + `\lib\time\zoneinfo\` + name); err == nil {
z.name = name
return z, nil
}
return nil, errors.New("unknown time zone " + name)
}
Windows環境では、システムレジストリからタイムゾーン情報を取得する独自のメカニズムがありますが、このコミットにより、$GOROOT
内のzoneinfoファイルも探索するようになりました。これにより、Windows上でもGoアプリケーションがより確実にタイムゾーン情報を取得できるようになります。
関連リンク
- GitHub Issue #2964: https://github.com/golang/go/issues/2964
- Go CL 5656101: https://golang.org/cl/5656101