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

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

このコミットは、Go言語の標準ライブラリ time パッケージにおけるWindows固有のタイムゾーン情報処理に関するバグ修正です。具体的には、src/pkg/time/zoneinfo_windows.go ファイル内のタイムゾーンオフセット計算ロジックが修正され、Windows環境でのタイムゾーン情報の取り扱いがより正確になりました。

コミット

commit 97197a6248e5239fc3491f8acbd1dccc5ec3d509
Author: Russ Cox <rsc@golang.org>
Date:   Wed Nov 30 15:45:24 2011 -0500

    time: fix windows build
    
    TBR=brainman
    CC=golang-dev
    https://golang.org/cl/5447057

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

https://github.com/golang/go/commit/97197a6248e5239fc3491f8acbd1dccc5ec3d509

元コミット内容

    time: fix windows build
    
    TBR=brainman
    CC=golang-dev
    https://golang.org/cl/5447057

変更の背景

このコミットは、Go言語の time パッケージがWindows環境でタイムゾーン情報を正しく処理できない問題を修正するために行われました。特に、Windows APIから取得されるタイムゾーン情報(TIME_ZONE_INFORMATION構造体)の BiasStandardBiasDaylightBias フィールドの解釈と計算に誤りがあったことが原因と考えられます。

WindowsのタイムゾーンAPIは、UTCからのオフセットを計算するために複数のバイアス値を使用します。これらを正しく組み合わせないと、標準時と夏時間の切り替え時や、特定のタイムゾーンにおいて誤った時刻オフセットが適用される可能性がありました。この修正は、これらのバイアス値をGoの time パッケージが期待する形式(UTCからの秒数オフセット)に正確に変換することを目的としています。

前提知識の解説

タイムゾーンとオフセット

タイムゾーンは、地球上の特定の地域で共通して使用される標準時刻の領域です。各タイムゾーンは、協定世界時(UTC: Coordinated Universal Time)からのオフセット(ずれ)で定義されます。例えば、日本標準時(JST)はUTC+9時間です。

夏時間(Daylight Saving Time, DST)

夏時間とは、特定の期間(通常は夏の間)に時計を1時間進める制度です。これにより、日中の明るい時間を有効活用し、エネルギー消費を抑えるなどの目的があります。夏時間が適用される地域では、標準時と夏時間でUTCからのオフセットが変化します。

Windowsのタイムゾーン情報 (TIME_ZONE_INFORMATION 構造体)

Windows APIでは、GetTimeZoneInformation 関数などを用いてシステムに設定されているタイムゾーン情報を取得できます。この情報は TIME_ZONE_INFORMATION 構造体に格納されます。この構造体には、以下の重要なフィールドが含まれます。

  • Bias: ローカル時刻とUTCの間の差(分単位)です。これは、タイムゾーンの基本オフセットを表します。例えば、UTC-5時間のタイムゾーンであれば、Bias は300(分)となります。
  • StandardBias: 標準時が有効な期間に Bias に加算されるバイアス(分単位)です。通常、標準時は Bias のみで定義されるため、この値は0であることが多いですが、一部のタイムゾーンでは異なる場合があります。
  • DaylightBias: 夏時間(DST)が有効な期間に Bias に加算されるバイアス(分単位)です。通常、夏時間では時計が1時間進むため、この値は-60(分)となります。

これらのバイアス値は、UTCからのオフセットを計算するために以下のように使用されます。

  • 標準時オフセット: Bias + StandardBias
  • 夏時間オフセット: Bias + DaylightBias

Goの time パッケージは、これらの情報から最終的なUTCからの秒数オフセットを計算する必要があります。

Go言語のスライス ([]rune)

Go言語のスライスは、可変長シーケンスを扱うための強力なデータ構造です。make 関数で初期容量を指定して作成することもできますが、append 関数を使って要素を追加していくことで、必要に応じて動的にサイズを拡張することもできます。

rune はGo言語におけるUnicodeコードポイントを表す型で、通常は int32 のエイリアスです。文字列を []rune に変換することで、Unicode文字を正しく処理できます。

技術的詳細

このコミットの主要な変更点は、src/pkg/time/zoneinfo_windows.go ファイル内の initLocalFromTZI 関数におけるタイムゾーンオフセットの計算ロジックです。

以前の実装では、標準時オフセット (std.offset) を計算する際に i.StandardBias のみを考慮し、i.Bias を適切に組み込んでいませんでした。また、夏時間オフセット (dst.offset) の計算も、この誤った std.offset を基準にしていました。

新しい実装では、Windowsの TIME_ZONE_INFORMATION 構造体の BiasStandardBiasDaylightBias の各フィールドを正しく組み合わせて、UTCからの最終的なオフセットを計算するように修正されています。

具体的には:

  1. nzone == 1 の場合(夏時間がないタイムゾーン):
    • std.offset-int(i.Bias) * 60 と計算されます。これは、Bias のみがUTCからのオフセットを決定するためです。
  2. 夏時間があるタイムゾーンの場合:
    • std.offset-int(i.Bias+i.StandardBias) * 60 と計算されます。これは、標準時オフセットが BiasStandardBias の合計によって決定されるためです。
    • dst.offset-int(i.Bias+i.DaylightBias) * 60 と計算されます。これは、夏時間オフセットが BiasDaylightBias の合計によって決定されるためです。

これらの変更により、Windowsシステムから取得したタイムゾーン情報がGoの time パッケージ内で正確に解釈され、正しい時刻オフセットが適用されるようになりました。

また、abbrev 関数では、make([]rune, len(name)) でスライスを事前に確保してからインデックスでアクセスする代わりに、var short []rune で空のスライスを宣言し、append を使って要素を追加する方式に変更されています。これは機能的な変更ではなく、よりGoらしいイディオムに合わせたコードスタイルの改善と考えられます。append は必要に応じてスライスの基盤となる配列を再割り当てするため、事前に正確なサイズが不明な場合や、要素数が少ない場合に便利です。

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

--- a/src/pkg/time/zoneinfo_windows.go
+++ b/src/pkg/time/zoneinfo_windows.go
@@ -28,12 +28,10 @@ func abbrev(name []uint16) string {
 	//
 	// http://social.msdn.microsoft.com/Forums/eu/vclanguage/thread/a87e1d25-fb71-4fe0-ae9c-a9578c9753eb
 	// http://stackoverflow.com/questions/4195948/windows-time-zone-abbreviations-in-asp-net
-	short := make([]rune, len(name))
-	w := 0
+	var short []rune
 	for _, c := range name {
 		if 'A' <= c && c <= 'Z' {
-			short[w] = rune(c)
-			w++
+			short = append(short, rune(c))
 		}
 	}\n 	return string(short)
 @@ -78,18 +76,23 @@ func initLocalFromTZI(i *syscall.Timezoneinformation) {
 
 	std := &l.zone[0]
 	std.name = abbrev(i.StandardName[0:])
-	std.offset = -int(i.StandardBias) * 60
 	if nzone == 1 {
 		// No daylight savings.
+		std.offset = -int(i.Bias) * 60
 		l.cacheStart = -1 << 63
 		l.cacheEnd = 1<<63 - 1
 		l.cacheZone = std
 		return
 	}
 
+	// StandardBias must be ignored if StandardDate is not set,
+	// so this computation is delayed until after the nzone==1
+	// return above.
+	std.offset = -int(i.Bias+i.StandardBias) * 60
+
 	dst := &l.zone[1]
 	dst.name = abbrev(i.DaylightName[0:])
-	dst.offset = std.offset + -int(i.DaylightBias)*60
+	dst.offset = -int(i.Bias+i.DaylightBias) * 60
 	dst.isDST = true
 
 	// Arrange so that d0 is first transition date, d1 second,

コアとなるコードの解説

abbrev 関数

  • 変更前:
    short := make([]rune, len(name))
    w := 0
    for _, c := range name {
        if 'A' <= c && c <= 'Z' {
            short[w] = rune(c)
            w++
        }
    }
    
    name スライスの長さに基づいて rune スライス short を事前に確保し、w というインデックス変数を使って要素を格納していました。これは有効な方法ですが、name の長さが非常に大きい場合、不要なメモリを確保する可能性があります。
  • 変更後:
    var short []rune
    for _, c := range name {
        if 'A' <= c && c <= 'Z' {
            short = append(short, rune(c))
        }
    }
    
    short を空のスライスとして宣言し、ループ内で条件に合う文字が見つかるたびに append 関数を使って要素を追加しています。append は必要に応じてスライスの基盤となる配列を自動的に拡張するため、より動的で柔軟なスライス構築方法です。この変更は、機能的な修正というよりも、Go言語のイディオムに合わせたコードの改善です。

initLocalFromTZI 関数

この関数は、Windows APIから取得した syscall.Timezoneinformation 構造体(i)を使用して、ローカルタイムゾーン情報(l)を初期化します。

  • 標準時オフセット (std.offset) の計算修正:

    • 変更前: std.offset = -int(i.StandardBias) * 60
      • これは StandardBias のみを使用してオフセットを計算しており、Bias を考慮していませんでした。Windowsのタイムゾーン情報では、基本オフセットである BiasStandardBiasDaylightBias を加算して最終的なオフセットを計算するのが正しい方法です。
    • 変更後:
      • nzone == 1 の場合(夏時間がないタイムゾーン): std.offset = -int(i.Bias) * 60
        • 夏時間がない場合、標準時オフセットは Bias のみで決定されます。
      • それ以外の場合(夏時間があるタイムゾーン): std.offset = -int(i.Bias+i.StandardBias) * 60
        • 標準時オフセットは BiasStandardBias の合計によって決定されます。
      • この修正により、Bias が正しくオフセット計算に組み込まれるようになりました。
  • 夏時間オフセット (dst.offset) の計算修正:

    • 変更前: dst.offset = std.offset + -int(i.DaylightBias)*60
      • これは、誤って計算された std.offset を基準にして DaylightBias を加算していました。
    • 変更後: dst.offset = -int(i.Bias+i.DaylightBias) * 60
      • 夏時間オフセットは BiasDaylightBias の合計によって直接計算されるようになりました。これにより、夏時間時の正しいUTCからのオフセットが得られます。
  • コメントの追加:

    // StandardBias must be ignored if StandardDate is not set,
    // so this computation is delayed until after the nzone==1
    // return above.
    

    このコメントは、StandardBiasStandardDate が設定されていない場合に無視されるべきであるというWindows APIの挙動を説明しています。そのため、nzone == 1 のケース(夏時間がない、つまり StandardDateDaylightDate が設定されていないケース)を先に処理し、その後に Bias + StandardBias の計算を行うというロジックの理由を明確にしています。

これらの変更により、Goの time パッケージがWindows環境でタイムゾーン情報をより正確に処理できるようになり、特にUTCからのオフセット計算におけるバグが修正されました。

関連リンク

参考にした情報源リンク