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

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

このコミットは、Go言語の time パッケージにおいて、Windows環境でのタイムゾーン略語の提供を改善することを目的としています。具体的には、Unicode CLDR (Common Locale Data Repository) の windowsZones.xml データを利用して、Windowsのタイムゾーン名に対応する標準時と夏時間の略語を生成する仕組みを導入しています。これにより、Windows上での時間表示の正確性とユーザビリティが向上します。

コミット

commit 07ea243d50e5e4c27e0212d45fa4e80d649d179f
Author: Alex Brainman <alex.brainman@gmail.com>
Date:   Thu Jun 6 16:30:25 2013 +1000

    time: provide timezone abbreviations on windows
    
    Use http://unicode.org/cldr/data/common/supplemental/windowsZones.xml
    to generate the map.
    
    Fixes #4838.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/9997043

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

https://github.com/golang/go/commit/07ea243d50e5e4c27e0212d45fa4e80d649d179f

元コミット内容

このコミットは、Go言語の time パッケージがWindows上でタイムゾーン略語(例: PST, EST, JSTなど)を正確に提供できるようにするための変更です。これまでは、WindowsのAPIがタイムゾーン略語を直接提供しないため、Goはタイムゾーン名の頭文字を抽出するなどの簡易的な方法で略語を生成していました。しかし、これは常に正確であるとは限りませんでした。

このコミットでは、Unicode CLDRの windowsZones.xml ファイルからデータを取得し、Windowsのタイムゾーン名とIANA (Internet Assigned Numbers Authority) タイムゾーンデータベースのゾーン名、そしてそれに対応する標準時と夏時間の略語のマッピングを生成する新しいツール genzabbrs.go を導入しています。生成されたマッピングは zoneinfo_abbrs_windows.go に組み込まれ、time パッケージがWindows上でより正確なタイムゾーン略語を提供できるようになります。

この変更は、GoのIssue #4838を修正するものです。

変更の背景

Windowsオペレーティングシステムは、タイムゾーン情報を管理するための独自のメカニズムを持っています。しかし、WindowsのタイムゾーンAPIは、タイムゾーンの正式名称(例: "Pacific Standard Time")は提供するものの、その略語(例: "PST"や"PDT")を直接的かつ一貫して提供することには課題がありました。これは、タイムゾーン略語が標準化されておらず、同じ略語が異なるタイムゾーンを指す場合がある(例: "CST"が中国標準時、中部標準時、キューバ標準時など)ため、曖昧さを避けるためにWindowsが意図的に略語の提供を避けていた側面があります。

Go言語の time パッケージは、プラットフォームに依存しない形で時間とタイムゾーンを扱うことを目指していますが、Windows環境ではこの略語の問題が顕在化していました。これまでのGoの実装では、Windowsのタイムゾーン名から大文字を抽出して略語を生成するなどのフォールバックメカニズムが用いられていましたが、これは正確性に欠け、ユーザーにとって混乱を招く可能性がありました。

このコミットは、この問題を解決し、Windowsユーザーに対しても正確なタイムゾーン略語を提供することで、Goアプリケーションの国際化対応とユーザビリティを向上させることを目的としています。

前提知識の解説

タイムゾーンと略語

  • タイムゾーン: 地球上の特定の地域で共通して使用される標準時の領域です。UTC(協定世界時)からのオフセットで定義されます。
  • タイムゾーン略語: タイムゾーンを簡潔に表すために使用される短い文字列です(例: JST, PST, EST, GMTなど)。しかし、略語は一意性が保証されず、同じ略語が異なるタイムゾーンを指す場合があるため、プログラミングにおいては完全なタイムゾーン名(IANAタイムゾーンIDなど)を使用することが推奨されます。

夏時間 (Daylight Saving Time, DST)

  • 夏時間とは、夏の間、日照時間を有効活用するために標準時を1時間進める制度です。これにより、タイムゾーン略語も変更されることがあります(例: PSTからPDTへ)。

IANA タイムゾーンデータベース (tz database / Olson database)

  • 世界中のタイムゾーン情報(オフセット、夏時間のルール、歴史的変更など)をまとめた標準的なデータベースです。各タイムゾーンは「地域/都市名」の形式で一意の識別子を持ちます(例: America/Los_Angeles, Asia/Tokyo)。多くのオペレーティングシステムやプログラミング言語で利用されています。

Unicode CLDR (Common Locale Data Repository)

  • Unicode Consortiumが提供する、国際化および地域化(I18N/L10N)のためのデータリポジトリです。言語、地域、通貨、日付、時刻、数値の書式設定など、多岐にわたるロケールデータが含まれています。
  • windowsZones.xml: CLDRの一部として提供されるXMLファイルで、Windowsのタイムゾーン名とIANAタイムゾーンIDとの間のマッピング情報を含んでいます。WindowsのタイムゾーンはIANAタイムゾーンよりも粒度が粗い場合があるため、このマッピングは重要です。このファイルは、Windowsのタイムゾーン名をIANAタイムゾーンIDに変換したり、その逆を行ったりする際に利用されます。

Go言語の time パッケージ

  • Go言語の標準ライブラリの一部で、時間、日付、期間、タイムゾーンなどを扱うための機能を提供します。time.Time 型は特定の時点を表し、time.Location 型はタイムゾーン情報をカプセル化します。

技術的詳細

このコミットの主要な技術的アプローチは、Windowsのタイムゾーン略語の課題を解決するために、外部の信頼できるデータソースであるUnicode CLDRの windowsZones.xml を利用することです。

  1. genzabbrs.go の導入:

    • この新しいGoプログラムは、windowsZones.xml からデータをダウンロードし、解析します。
    • windowsZones.xml は、Windowsのタイムゾーン名(例: "Pacific Standard Time")と、それに対応するIANAタイムゾーンID(例: "America/Los_Angeles")のマッピングを提供します。
    • genzabbrs.go は、各IANAタイムゾーンIDに対して、Goの time パッケージの機能を利用して、そのタイムゾーンの標準時と夏時間の略語を動的に取得します。これは、特定のタイムゾーンにおいて、年の異なる時期(例えば、1月と7月)の time.Zone() メソッドを呼び出すことで、標準時と夏時間の略語とオフセットを比較して判別します。
    • 取得したWindowsタイムゾーン名、IANAタイムゾーン名、標準時略語、夏時間略語のペアを構造化し、ソートします。
    • 最終的に、これらの情報をGoのソースコードとして整形し、zoneinfo_abbrs_windows.go ファイルに出力します。この出力は、Goのマップ(map[string]abbr)として定義され、Windowsのタイムゾーン名をキーとして、標準時と夏時間の略語のペアを値として持ちます。
  2. zoneinfo_abbrs_windows.go の生成:

    • genzabbrs.go によって生成されるファイルで、Windowsのタイムゾーン名と対応する略語の静的なマッピングが含まれます。これにより、Goの time パッケージは、実行時に外部サービスにアクセスすることなく、正確な略語情報にアクセスできるようになります。
  3. zoneinfo_windows.go の変更:

    • 既存の zoneinfo_windows.go ファイルが修正され、abbrev 関数が更新されます。
    • 新しい abbrev 関数は、まず zoneinfo_abbrs_windows.go で定義されたマップ abbrs を参照し、Windowsのタイムゾーン名に対応する略語が存在するかどうかを確認します。
    • もしマップ内に対応する略語が見つかれば、その略語を使用します。
    • 見つからない場合(例えば、新しいタイムゾーンが追加されたが、windowsZones.xml のデータがまだ更新されていない場合など)は、従来のフォールバックメカニズム(タイムゾーン名から大文字を抽出する)を使用します。これにより、後方互換性と堅牢性が確保されます。
  4. テストの追加:

    • export_windows_test.gozoneinfo_windows_test.go にテストが追加され、新しい略語生成ロジックが正しく機能することを確認します。特に、オーストラリアのタイムゾーン(夏時間がある)を強制的に設定し、略語が正しく取得されるかどうかのテストが含まれています。

このアプローチにより、GoはWindows環境においても、より正確で信頼性の高いタイムゾーン略語を提供できるようになります。

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

このコミットでは、主に以下のファイルが変更または新規追加されています。

  • src/pkg/time/Makefile: genzabbrs ツールをビルドし、zoneinfo_abbrs_windows.go を生成するためのルールが追加されました。
  • src/pkg/time/export_windows_test.go: テスト用のヘルパー関数 ForceAusForTesting が追加されました。
  • src/pkg/time/genzabbrs.go: Windowsのタイムゾーン略語を生成するための新しいGoプログラムです。windowsZones.xml からデータを読み込み、略語マップを生成します。
  • src/pkg/time/zoneinfo_abbrs_windows.go: genzabbrs.go によって生成されるファイルで、Windowsのタイムゾーン名と略語のマッピングがGoのコードとして含まれます。
  • src/pkg/time/zoneinfo_windows.go: Windows固有のタイムゾーン情報処理ロジックが含まれるファイルです。abbrev 関数が変更され、生成された略語マップを利用するようになりました。
  • src/pkg/time/zoneinfo_windows_test.go: Windows環境でのタイムゾーン略語のテストが追加されました。

src/pkg/time/genzabbrs.go (新規追加)

このファイルは、windowsZones.xml からタイムゾーンデータを取得し、Goのコードとして略語マップを生成するスクリプトです。

// getAbbrs finds timezone abbreviations (standard and daylight saving time)
// for location l.
func getAbbrs(l *time.Location) (st, dt string) {
	t := time.Date(time.Now().Year(), 0, 0, 0, 0, 0, 0, l)
	abbr1, off1 := t.Zone()
	for i := 0; i < 12; i++ {
		t = t.AddDate(0, 1, 0)
		abbr2, off2 := t.Zone()
		if abbr1 != abbr2 {
			if off2-off1 < 0 { // southern hemisphere
				abbr1, abbr2 = abbr2, abbr1
			}
			return abbr1, abbr2
		}
	}
	return abbr1, abbr1
}

// ... (XML parsing and data structure definitions) ...

func readWindowsZones() (zones, error) {
	// ... (HTTP GET for wzURL, XML unmarshaling) ...
	for _, z := range sd.Zones {
		// ... (LoadLocation, getAbbrs, append to zones) ...
	}
	return zs, nil
}

func main() {
	// ... (readWindowsZones, sort, template execution to os.Stdout) ...
}

const prog = `
// ... (Go code template for zoneinfo_abbrs_windows.go) ...
var abbrs = map[string]abbr{
{{range .Zs}}	"{{.WinName}}": {"{{.StTime}}", "{{.DSTime}}"}, // {{.UnixName}}
{{end}}}
`

src/pkg/time/zoneinfo_windows.go (変更)

abbrev 関数のロジックが変更され、生成された略語マップ abbrs を利用するようになりました。

// abbrev returns the abbreviations to use for the given zone z.
func abbrev(z *syscall.Timezoneinformation) (std, dst string) {
	stdName := syscall.UTF16ToString(z.StandardName[:])
	a, ok := abbrs[stdName] // ここで生成されたマップを参照
	if !ok {
		// fallback to using capital letters
		dstName := syscall.UTF16ToString(z.DaylightName[:])
		return extractCAPS(stdName), extractCAPS(dstName)
	}
	return a.std, a.dst
}

// ... (initLocalFromTZI function updated to use new abbrev) ...

コアとなるコードの解説

このコミットの核心は、Windowsのタイムゾーン略語の取得における課題を、外部の信頼できるデータソースとGoの既存のタイムゾーン処理能力を組み合わせることで解決している点にあります。

  1. genzabbrs.go の役割:

    • このスクリプトは、Goのビルドプロセスの一部として実行されることを想定しています(Makefile にそのためのルールが追加されています)。
    • http.Get(wzURL)windowsZones.xml をダウンロードし、xml.Unmarshal でXMLデータをGoの構造体にパースします。
    • パースされたデータには、Windowsのタイムゾーン名(WinName)と、それに対応するIANAタイムゾーンID(Type、ここではUnixNameとして扱われる)が含まれています。
    • time.LoadLocation(z.Type) を使用して、各IANAタイムゾーンIDに対応する time.Location オブジェクトをロードします。
    • getAbbrs 関数が重要です。この関数は、与えられた time.Location に対して、そのタイムゾーンの標準時と夏時間の略語を特定します。これは、現在の年を基準に、異なる月(例えば1月と7月)の time.Zone() メソッドを呼び出し、返される略語とオフセットを比較することで行われます。夏時間があるタイムゾーンでは、異なる時期に異なる略語が返されることを利用しています。南半球のタイムゾーンでは夏時間の開始と終了が逆になるため、オフセットの差をチェックして略語の順序を調整しています。
    • 最終的に、text/template を使用して、取得した略語情報をGoのソースコード形式(zoneinfo_abbrs_windows.go の内容)に整形し、標準出力に出力します。この出力は、Makefile によってファイルにリダイレクトされます。
  2. zoneinfo_abbrs_windows.go の生成と利用:

    • このファイルは、genzabbrs.go によって生成される静的なデータファイルです。Goの time パッケージの一部としてコンパイルされます。
    • abbrs という名前の map[string]abbr 型のグローバル変数を定義しており、キーはWindowsのタイムゾーン名(例: "Pacific Standard Time")、値は標準時略語と夏時間略語を含む abbr 構造体です。
    • zoneinfo_windows.go 内の abbrev 関数は、この abbrs マップをルックアップテーブルとして利用します。これにより、Windowsのタイムゾーン情報から直接略語を取得できるようになります。
  3. フォールバックメカニズム:

    • abbrev 関数内で abbrs マップにエントリが見つからなかった場合(!ok)、従来の extractCAPS 関数(タイムゾーン名から大文字を抽出する)にフォールバックします。これは、CLDRデータがまだ更新されていない新しいタイムゾーンや、何らかの理由でマッピングが存在しない場合に備えた堅牢な設計です。

この一連の変更により、GoはWindows環境においても、より正確で国際的に認識されているタイムゾーン略語を提供できるようになり、Goアプリケーションのタイムゾーン処理の信頼性が向上します。

関連リンク

参考にした情報源リンク