[インデックス 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
構造体)の Bias
、StandardBias
、DaylightBias
フィールドの解釈と計算に誤りがあったことが原因と考えられます。
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
構造体の Bias
、StandardBias
、DaylightBias
の各フィールドを正しく組み合わせて、UTCからの最終的なオフセットを計算するように修正されています。
具体的には:
nzone == 1
の場合(夏時間がないタイムゾーン):std.offset
は-int(i.Bias) * 60
と計算されます。これは、Bias
のみがUTCからのオフセットを決定するためです。
- 夏時間があるタイムゾーンの場合:
std.offset
は-int(i.Bias+i.StandardBias) * 60
と計算されます。これは、標準時オフセットがBias
とStandardBias
の合計によって決定されるためです。dst.offset
は-int(i.Bias+i.DaylightBias) * 60
と計算されます。これは、夏時間オフセットがBias
とDaylightBias
の合計によって決定されるためです。
これらの変更により、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のタイムゾーン情報では、基本オフセットであるBias
にStandardBias
やDaylightBias
を加算して最終的なオフセットを計算するのが正しい方法です。
- これは
- 変更後:
nzone == 1
の場合(夏時間がないタイムゾーン):std.offset = -int(i.Bias) * 60
- 夏時間がない場合、標準時オフセットは
Bias
のみで決定されます。
- 夏時間がない場合、標準時オフセットは
- それ以外の場合(夏時間があるタイムゾーン):
std.offset = -int(i.Bias+i.StandardBias) * 60
- 標準時オフセットは
Bias
とStandardBias
の合計によって決定されます。
- 標準時オフセットは
- この修正により、
Bias
が正しくオフセット計算に組み込まれるようになりました。
- 変更前:
-
夏時間オフセット (
dst.offset
) の計算修正:- 変更前:
dst.offset = std.offset + -int(i.DaylightBias)*60
- これは、誤って計算された
std.offset
を基準にしてDaylightBias
を加算していました。
- これは、誤って計算された
- 変更後:
dst.offset = -int(i.Bias+i.DaylightBias) * 60
- 夏時間オフセットは
Bias
とDaylightBias
の合計によって直接計算されるようになりました。これにより、夏時間時の正しいUTCからのオフセットが得られます。
- 夏時間オフセットは
- 変更前:
-
コメントの追加:
// StandardBias must be ignored if StandardDate is not set, // so this computation is delayed until after the nzone==1 // return above.
このコメントは、
StandardBias
がStandardDate
が設定されていない場合に無視されるべきであるというWindows APIの挙動を説明しています。そのため、nzone == 1
のケース(夏時間がない、つまりStandardDate
やDaylightDate
が設定されていないケース)を先に処理し、その後にBias + StandardBias
の計算を行うというロジックの理由を明確にしています。
これらの変更により、Goの time
パッケージがWindows環境でタイムゾーン情報をより正確に処理できるようになり、特にUTCからのオフセット計算におけるバグが修正されました。
関連リンク
- Go言語の
time
パッケージに関する公式ドキュメント: https://pkg.go.dev/time - Windows
TIME_ZONE_INFORMATION
構造体に関するMicrosoftドキュメント: https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/ns-timezoneapi-time_zone_information
参考にした情報源リンク
- Go Change-Id: I2222222222222222222222222222222222222222 (Goの内部的な変更管理システムにおけるID)
- Go CL 5447057: https://golang.org/cl/5447057 (このコミットに対応するGoのチェンジリスト)
- Stack Overflow: Windows time zone abbreviations in ASP.NET: https://stackoverflow.com/questions/4195948/windows-time-zone-abbreviations-in-asp-net (
abbrev
関数内のコメントで参照されているStack Overflowの質問) - MSDN Forum: http://social.msdn.microsoft.com/Forums/eu/vclanguage/thread/a87e1d25-fb71-4fe0-ae9c-a9578c9753eb (
abbrev
関数内のコメントで参照されているMSDNフォーラムのスレッド) - Go言語の
append
関数に関するドキュメント: https://pkg.go.dev/builtin#append - Go言語の
rune
型に関するドキュメント: https://pkg.go.dev/builtin#rune