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

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

このコミットは、Go言語のtimeパッケージにおけるタイムゾーンデータの検索パスに関する修正と、そのフォールバックメカニズムのテスト強化を目的としています。特にUnixシステムにおいて、タイムゾーン情報がZIPファイルから読み込まれるフォールバックパスが正しくテストされるように、パスの定義とテストコードが調整されました。

コミット

commit a508381e89523c6a1682a1498b196d4262a1504a
Author: Russ Cox <rsc@golang.org>
Date:   Tue Oct 29 17:11:51 2013 -0400

    time: correct path to time zone zip file on Unix
    
    Most Unix systems have their own time zone data,
    so we almost never get far enough in the list to
    discover that we cannot fall back to the zip file.
    Adjust testing to exercise the final fallback.
    
    Plan 9 and Windows were already correct
    (and are the main users of the zip file).
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/19280043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/a508381e89523c6a1682a1498b196d4262a1504a

元コミット内容

Goのtimeパッケージにおいて、UnixシステムでのタイムゾーンZIPファイルへのパスを修正し、テストを調整して最終的なフォールバックメカニズムが機能することを確認する。ほとんどのUnixシステムは独自のタイムゾーンデータを持っているため、ZIPファイルへのフォールバックパスがテストされる機会がほとんどなかった。Plan 9とWindowsは既に正しく、主にZIPファイルを使用している。

変更の背景

Go言語のtimeパッケージは、システムのタイムゾーン情報を利用して時刻の変換や表示を行います。この情報源として、Goは複数のパスを探索し、適切なタイムゾーンデータファイルを見つけようとします。一般的なUnix系システムでは、/usr/share/zoneinfo/のような標準的なディレクトリにタイムゾーンデータが配置されています。しかし、これらのパスからデータが見つからない場合、Goは自身の配布物に含まれるタイムゾーン情報ZIPファイル(zoneinfo.zip)にフォールバックするメカニズムを持っています。

このコミット以前は、Unixシステムにおいて、このzoneinfo.zipへのパスが正しく設定されていなかったか、あるいはテストが不十分であったため、フォールバックメカニズムが実際に機能するかどうかの検証が困難でした。コミットメッセージによると、「ほとんどのUnixシステムは独自のタイムゾーンデータを持っているため、ZIPファイルにフォールバックできないことを発見するほどリストの奥深くまで到達することはほとんどない」という状況でした。つまり、通常の使用ではシステム提供のタイムゾーンデータが使われるため、Goが自身のZIPファイルに頼るシナリオが稀であり、結果としてそのフォールバックパスが壊れていても気づかれにくい状態だったのです。

この変更の目的は、Unix環境におけるzoneinfo.zipへのパスを修正し、さらに、システム提供のタイムゾーンデータが見つからない状況を意図的に作り出すテストを追加することで、この重要なフォールバックメカニズムが確実に機能することを保証することです。これにより、Goアプリケーションが様々な環境でタイムゾーン情報を正しく扱えるよう、堅牢性が向上します。

前提知識の解説

  • Go言語のtimeパッケージ: Go言語の標準ライブラリの一部で、時刻の表現、計算、フォーマット、タイムゾーンの処理などを担当します。time.Location型は特定のタイムゾーンを表し、LoadLocation関数などを使ってタイムゾーン情報をロードします。
  • タイムゾーンデータ (zoneinfo): 世界中のタイムゾーンに関する情報(オフセット、夏時間規則など)を定義したデータセットです。IANA (Internet Assigned Numbers Authority) が管理するTZ database (またはzoneinfo database) が広く使われています。Unix系システムでは通常、/usr/share/zoneinfoなどのディレクトリにバイナリ形式で格納されています。
  • zoneinfo.zip: Go言語の配布物に含まれるタイムゾーンデータのZIPアーカイブです。システムにタイムゾーンデータが存在しない場合や、特定の環境(例: Windows、Plan 9)でシステム提供のデータが利用できない場合に、Goがフォールバックとして使用します。
  • runtime.GOROOT(): Go言語のランタイム関数で、Goのインストールルートディレクトリのパスを返します。Goの標準ライブラリやツールがどこにインストールされているかを知るために使用されます。
  • フォールバックメカニズム: ある主要な方法が失敗した場合に、代替の方法を試みる仕組みです。この文脈では、システム提供のタイムゾーンデータが見つからない場合に、Go自身のzoneinfo.zipを使用するメカニズムを指します。
  • テスト駆動開発 (TDD) とテストの重要性: このコミットでは、既存のコードの修正だけでなく、その修正が意図通りに機能し、将来にわたって回帰しないことを保証するためのテストが追加されています。特に、通常では発生しにくい「フォールバック」のシナリオを強制的に発生させるテストは、システムの堅牢性を高める上で非常に重要です。

技術的詳細

このコミットの主要な変更点は、Unixシステムにおけるタイムゾーンデータの検索パスの修正と、そのフォールバックパスをテストするためのメカニズムの導入です。

Goのtimeパッケージは、zoneDirsという文字列スライスに定義されたディレクトリパスを順番に探索してタイムゾーンデータを探します。このコミット以前のzoneinfo_unix.goでは、runtime.GOROOT() + "/lib/time/zoneinfo/"というパスがリストに含まれていました。しかし、Goの配布物に含まれるタイムゾーンデータは通常、zoneinfo.zipという単一のZIPファイルとして提供されており、ディレクトリではありませんでした。このため、Unixシステムでシステム提供のタイムゾーンデータが見つからなかった場合でも、Goは自身のzoneinfo.zipを正しく見つけることができませんでした。

このコミットでは、zoneinfo_unix.go内のzoneDirsスライスにおいて、runtime.GOROOT() + "/lib/time/zoneinfo/"というエントリをruntime.GOROOT() + "/lib/time/zoneinfo.zip"に修正しました。これにより、Goは自身の配布物に含まれるZIPファイルを正しく検索対象に含めることができるようになりました。

さらに、このフォールバックメカニズムが実際に機能することをテストするために、forceZipFileForTestingというテストヘルパー関数が導入されました。この関数は、テスト中にzoneDirsスライスを一時的に変更し、システム提供のタイムゾーンデータパスを意図的に無効化(/XXXNOEXISTのような存在しないパスに設定)することで、Goが強制的にzoneinfo.zipにフォールバックする状況を作り出します。これにより、通常ではテストが難しいフォールバックシナリオを確実に検証できるようになりました。

export_test.goでは、このforceZipFileForTesting関数がテストパッケージからアクセスできるようにエクスポートされています。そして、time_test.goにはTestLoadLocationZipFileという新しいテストが追加され、forceZipFileForTesting(true)を呼び出してフォールバックを強制し、LoadLocation("Australia/Sydney")がエラーなく成功することを確認しています。テストの終了時にはdefer ForceZipFileForTesting(false)で元の状態に戻すことで、他のテストへの影響を防いでいます。

Plan 9とWindows向けのzoneinfo_plan9.gozoneinfo_windows.goにもforceZipFileForTesting関数が追加されていますが、これらのOSでは元々ZIPファイルのみを使用しているため、関数の中身は空(no-op)となっています。これは、クロスプラットフォームなテストヘルパーのインターフェースを統一するための措置です。

コアとなるコードの変更箇所

このコミットで変更された主要なファイルとコードスニペットは以下の通りです。

  1. src/pkg/time/zoneinfo_unix.go:

    --- a/src/pkg/time/zoneinfo_unix.go
    +++ b/src/pkg/time/zoneinfo_unix.go
    @@ -32,7 +32,19 @@ var zoneDirs = []string{
     	"/usr/share/zoneinfo/",
     	"/usr/share/lib/zoneinfo/",
     	"/usr/lib/locale/TZ/",
    -	runtime.GOROOT() + "/lib/time/zoneinfo/",
    +	runtime.GOROOT() + "/lib/time/zoneinfo.zip",
     }
     
    +var origZoneDirs = zoneDirs
    +
    +func forceZipFileForTesting(zipOnly bool) {
    +	zoneDirs = make([]string, len(origZoneDirs))
    +	copy(zoneDirs, origZoneDirs)
    +	if zipOnly {
    +		for i := 0; i < len(zoneDirs)-1; i++ {
    +			zoneDirs[i] = "/XXXNOEXIST"
    +		}
    +	}
    +}
     
     func initLocal() {
    
  2. src/pkg/time/export_test.go:

    --- a/src/pkg/time/export_test.go
    +++ b/src/pkg/time/export_test.go
    @@ -18,4 +18,7 @@ func ForceUSPacificForTesting() {
      	localOnce.Do(initTestingZone)
      }
      
    -var ParseTimeZone = parseTimeZone
    +var (
    +	ForceZipFileForTesting = forceZipFileForTesting
    +	ParseTimeZone          = parseTimeZone
    +)
    
  3. src/pkg/time/time_test.go:

    --- a/src/pkg/time/time_test.go
    +++ b/src/pkg/time/time_test.go
    @@ -578,6 +578,16 @@ func TestParseInSydney(t *testing.T) {
      	}
      }
      
    +func TestLoadLocationZipFile(t *testing.T) {
    +	ForceZipFileForTesting(true)
    +	defer ForceZipFileForTesting(false)
    +
    +	_, err := LoadLocation("Australia/Sydney")
    +	if err != nil {
    +		t.Fatal(err)
    +	}
    +}
    +
      var rubyTests = []ParseTest{
      	{"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010", true, true, 1, 0},
      	// Ignore the time zone in the test. If it parses, it'll be OK.
    
  4. src/pkg/time/zoneinfo_plan9.go および src/pkg/time/zoneinfo_windows.go: これらのファイルには、forceZipFileForTesting関数が追加されましたが、Plan 9とWindowsでは元々ZIPファイルのみを使用するため、その実装は空です。

    // Example from zoneinfo_plan9.go
    func forceZipFileForTesting(zipOnly bool) {
    	// We only use the zip file anyway.
    }
    

コアとなるコードの解説

  • src/pkg/time/zoneinfo_unix.go の変更:

    • zoneDirs変数の変更: runtime.GOROOT() + "/lib/time/zoneinfo/"runtime.GOROOT() + "/lib/time/zoneinfo.zip"に修正されました。これは、Goの配布物に含まれるタイムゾーンデータがディレクトリではなくZIPファイルとして提供されているため、正しいパスに修正されたことを意味します。これにより、Unixシステムでシステム提供のタイムゾーンデータが見つからない場合に、Goが自身のzoneinfo.zipを正しく見つけられるようになります。
    • origZoneDirs変数の追加: zoneDirsの元の状態を保存するために導入されました。これは、テスト中にzoneDirsを変更した後、元の状態に戻すために使用されます。
    • forceZipFileForTesting関数の追加: この関数はテスト目的で導入されました。zipOnlytrueの場合、zoneDirsの最初のlen(zoneDirs)-1個のエントリを/XXXNOEXISTという存在しないパスに設定します。これにより、Goがシステム提供のタイムゾーンデータを見つけられなくなり、強制的にリストの最後の要素であるzoneinfo.zipにフォールバックするシナリオをシミュレートできます。
  • src/pkg/time/export_test.go の変更:

    • ForceZipFileForTesting変数の追加: forceZipFileForTesting関数をテストパッケージからアクセスできるようにエクスポートしています。Goでは、内部の(アンエクスポートされた)関数をテスト目的で外部に公開するために、_test.goファイル内でエクスポートされた変数に代入する慣習があります。
  • src/pkg/time/time_test.go の変更:

    • TestLoadLocationZipFileテスト関数の追加:
      • ForceZipFileForTesting(true)を呼び出すことで、タイムゾーンデータの検索パスを操作し、zoneinfo.zipへのフォールバックを強制します。
      • defer ForceZipFileForTesting(false)は、テストが終了した後にzoneDirsを元の状態に戻すためのものです。これにより、このテストが他のテストに影響を与えることを防ぎます。
      • LoadLocation("Australia/Sydney")を呼び出し、エラーが発生しないことを確認します。これは、フォールバックメカニズムが正しく機能し、zoneinfo.zipからタイムゾーン情報がロードできることを検証しています。
  • src/pkg/time/zoneinfo_plan9.go および src/pkg/time/zoneinfo_windows.go の変更:

    • これらのファイルにもforceZipFileForTesting関数が追加されましたが、その実装は空です。これは、Plan 9とWindowsでは元々タイムゾーンデータの主要なソースとしてzoneinfo.zipを使用しているため、Unixのようにパスを操作してフォールバックを強制する必要がないためです。この追加は、テストヘルパーのインターフェースを統一し、クロスプラットフォームなテストコードの記述を容易にするためのものです。

これらの変更により、GoのtimeパッケージはUnixシステムにおいてもzoneinfo.zipへのフォールバックが正しく機能するようになり、その動作がテストによって保証されるようになりました。

関連リンク

参考にした情報源リンク