[インデックス 18012] ファイルの概要
このコミットは、Go言語のtime
パッケージにおけるWindows環境でのタイムゾーン情報取得に関するバグ修正です。特に、中国語版Windows XPシステムにおいて、夏時間(Daylight Saving Time, DST)を使用しない地域でのタイムゾーン名の比較ロジックが原因で発生していたテストエラーを解消します。
コミット
commit 98f16ad1123ffcdec1122a0f7b535b590e879423
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Tue Dec 17 02:43:14 2013 -0500
time: fix test error in Chinese edition of Windows
On the Chinese Windows XP system that I'm using, GetTimeZoneInformation returns a struct containing "中国标准时间" (China Standard Time in Chinese) in both StandardName and DaylightName (which is correct, because China does not use DST). However, in registry, under key HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\China Standard Time, the key Std and Dlt contain "中国标准时间" (China Standard Time in Chinese) and "中国夏季时间" (China Summer Time in Chinese) respectively. This means that time.toEnglishName() cannot determine the abbreviation for the local timezone (CST) and causes test failures (time.Local is empty)
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/43210043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/98f16ad1123ffcdec1122a0f7b535b590e879423
元コミット内容
このコミットは、Go言語のtime
パッケージにおいて、Windows環境でのタイムゾーン情報取得に関するテストエラーを修正するものです。具体的には、中国語版Windows XPシステムでGetTimeZoneInformation
APIが返す情報と、レジストリに格納されているタイムゾーン情報との間に不整合がある場合に、time.toEnglishName()
関数がローカルタイムゾーンの略称(例: CST)を正しく判別できず、結果としてtime.Local
が空になりテストが失敗するという問題に対処しています。中国では夏時間(DST)が使用されないため、標準時と夏時間の名前が同じになることがありますが、レジストリには夏時間の名前が別途定義されていることが問題の原因でした。
変更の背景
この変更の背景には、Windowsオペレーティングシステムにおけるタイムゾーン情報の扱いの複雑さがあります。特に、夏時間(Daylight Saving Time, DST)を採用していない地域(例: 中国)では、標準時と夏時間の名前が論理的に同じになるはずです。しかし、Windowsのレジストリ(HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones
)には、各タイムゾーンの標準時名(Std
)と夏時間名(Dlt
)が個別に格納されています。
コミットメッセージによると、中国語版Windows XPシステムでは、GetTimeZoneInformation
APIが返す構造体において、StandardName
とDaylightName
の両方に「中国标准时间」(China Standard Time)が含まれていました。これは中国がDSTを使用しないため、正しい挙動です。しかし、レジストリのChina Standard Time
キーの下では、Std
には「中国标准时间」が、Dlt
には「中国夏季时间」(China Summer Time)が格納されていました。
Goのtime
パッケージ内のmatchZoneKey
関数(またはそれに類するロジック)は、これらの名前を比較してタイムゾーンの情報を特定しようとします。この際、夏時間名が標準時名と異なる場合に特定の処理を行うロジックが存在していました。中国のようにDSTを使用しない地域で、APIが返す情報とレジストリの情報が食い違うことで、この比較ロジックが誤動作し、タイムゾーンの略称を正しく判別できなくなり、結果としてtime.Local
が空になるというテストエラーが発生していました。
この修正は、このような特定の環境(中国語版Windows XP)におけるタイムゾーン情報の不整合を吸収し、time
パッケージが正しくローカルタイムゾーンを特定できるようにすることを目的としています。
前提知識の解説
このコミットを理解するためには、以下の前提知識が必要です。
-
タイムゾーンと夏時間 (Daylight Saving Time, DST):
- タイムゾーン: 地球上の特定の地域に適用される標準的な時刻の領域です。UTC(協定世界時)からのオフセットで定義されます。
- 夏時間 (DST): 特定の期間(通常は夏の間)に時計を1時間進める制度です。これにより、夕方の明るい時間を長く利用できます。DSTを採用している地域では、標準時と夏時間で異なるタイムゾーン名(例: EST/EDT - Eastern Standard Time/Eastern Daylight Time)が使用されることがあります。DSTを採用していない地域では、年間を通じて同じタイムゾーン名が使用されます。
-
Windowsにおけるタイムゾーン情報の管理:
GetTimeZoneInformation
API: Windows APIの一つで、現在のシステムに設定されているタイムゾーン情報を取得するために使用されます。このAPIはTIME_ZONE_INFORMATION
構造体を返します。この構造体には、標準時名(StandardName
)と夏時間名(DaylightName
)が含まれます。- Windowsレジストリ: Windowsオペレーティングシステムの設定情報が格納されている階層型データベースです。タイムゾーンに関する情報もレジストリの特定のパス(
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones
)に格納されています。各タイムゾーンキーの下には、標準時名(Std
)と夏時間名(Dlt
)などの値が含まれます。 - 不整合の可能性:
GetTimeZoneInformation
APIが返す情報とレジストリに格納されている情報が、常に完全に一致するとは限りません。特に、DSTを採用しない地域での名前の扱いにおいて、このような不整合が発生することがあります。
-
Go言語の
time
パッケージ:- Go言語の標準ライブラリの一部で、時刻と日付の操作、タイムゾーンの変換などを扱います。
time.Local
: システムのローカルタイムゾーンを表す*time.Location
型の変数です。この変数は、プログラムの起動時にシステムのタイムゾーン情報に基づいて初期化されます。time.Location
構造体: タイムゾーンに関する情報(名前、オフセット、DSTのルールなど)をカプセル化します。zoneinfo_windows.go
: Windows環境におけるタイムゾーン情報の取得と解析を担当するGoのソースファイルです。このファイルには、Windows APIを呼び出してタイムゾーン情報を取得し、Goのtime.Location
オブジェクトにマッピングするロジックが含まれています。
技術的詳細
このコミットの技術的詳細は、Windowsのタイムゾーン情報取得におけるエッジケースのハンドリングにあります。
WindowsのGetTimeZoneInformation
APIは、現在のタイムゾーン設定に関する情報をTIME_ZONE_INFORMATION
構造体として返します。この構造体には、標準時(Standard Time)と夏時間(Daylight Time)の表示名が含まれます。中国のように夏時間を使用しない地域では、論理的にはStandardName
とDaylightName
が同じ文字列(例: "中国标准时间")になるべきです。
しかし、Windowsレジストリのタイムゾーン設定(HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\<TimeZoneName>
)では、Std
(標準時名)とDlt
(夏時間名)が個別の値として格納されています。コミットメッセージが指摘するように、中国のタイムゾーンではStd
が「中国标准时间」であるのに対し、Dlt
は「中国夏季时间」と、異なる文字列が設定されている場合があります。これは、レジストリのデータが、たとえそのタイムゾーンが夏時間を使用しなくても、夏時間用の名前を保持している可能性があることを示しています。
Goのtime
パッケージ内のzoneinfo_windows.go
ファイルには、Windows APIやレジストリから取得したタイムゾーン情報を解析し、Goの内部表現にマッピングするロジックが含まれています。このロジックの中で、matchZoneKey
関数(またはその一部)は、レジストリから読み取った夏時間名(dltname
)と、APIから取得した夏時間名(dstname
)を比較する部分がありました。
元のコードでは、s != dstname
という条件で、レジストリの夏時間名(s
)がAPIの夏時間名(dstname
)と異なる場合に、マッチングを失敗させていました。このロジックは、通常、DSTが有効な地域で、APIとレジストリの名前が一致しない場合に誤ったタイムゾーンを特定しないようにするためのものです。
しかし、中国のようにDSTを使用しない地域では、APIのDaylightName
はStandardName
と同じ「中国标准时间」を返します。一方で、レジストリのDlt
は「中国夏季时间」を返すことがあります。この場合、「中国夏季时间」(s
) と 「中国标准时间」(dstname
) は異なるため、s != dstname
の条件が真となり、タイムゾーンの特定に失敗していました。
このコミットは、この比較ロジックに&& dstname != stdname
という条件を追加することで、この問題を解決しています。つまり、「レジストリの夏時間名がAPIの夏時間名と異なる」かつ「APIの夏時間名がAPIの標準時名とも異なる」場合にのみ、マッチングを失敗させるように変更しました。
これにより、中国のようにDSTを使用しない地域で、APIのStandardName
とDaylightName
が同じ(dstname == stdname
)である場合、たとえレジストリの夏時間名がAPIの夏時間名と異なっていても、マッチングが成功するようになります。これは、DSTを使用しない地域では、夏時間名が標準時名と同じであることが期待されるため、レジストリの夏時間名が形式的に異なる場合でも、それを許容するという判断に基づいています。
この修正により、time.Local
が正しく初期化され、関連するテストが中国語版Windows XPシステムでもパスするようになりました。
コアとなるコードの変更箇所
変更はsrc/pkg/time/zoneinfo_windows.go
ファイルの一箇所のみです。
--- a/src/pkg/time/zoneinfo_windows.go
+++ b/src/pkg/time/zoneinfo_windows.go
@@ -54,7 +54,7 @@ func matchZoneKey(zones syscall.Handle, kname string, stdname, dstname string) (\
if err != nil {
return false, err
}
- if s != dstname {
+ if s != dstname && dstname != stdname {
return false, nil
}
return true, nil
具体的には、54行目のif s != dstname {
がif s != dstname && dstname != stdname {
に変更されています。
コアとなるコードの解説
変更された行は、matchZoneKey
関数(またはその内部ロジック)の一部であり、Windowsレジストリから読み取ったタイムゾーン情報と、GetTimeZoneInformation
APIから取得したタイムゾーン情報を比較して、適切なタイムゾーンキーを特定する役割を担っています。
s
: これは、Windowsレジストリから読み取られた夏時間(Daylight Time)の表示名を表す文字列です。dstname
: これは、GetTimeZoneInformation
APIによって返されたTIME_ZONE_INFORMATION
構造体のDaylightName
フィールドの値、つまりAPIが認識する夏時間の表示名を表す文字列です。stdname
: これは、GetTimeZoneInformation
APIによって返されたTIME_ZONE_INFORMATION
構造体のStandardName
フィールドの値、つまりAPIが認識する標準時の表示名を表す文字列です。
変更前のコード:
if s != dstname {
return false, nil
}
この条件は、「レジストリの夏時間名(s
)がAPIの夏時間名(dstname
)と異なる場合、このタイムゾーンキーはマッチしない」という意味でした。これは、通常、APIとレジストリの情報が一致することを期待するロジックです。しかし、前述の通り、中国のようにDSTを使用しない地域では、APIのDaylightName
はStandardName
と同じになる一方で、レジストリのDlt
は異なる文字列を持つことがあり、この条件が誤って真となり、マッチングが失敗していました。
変更後のコード:
if s != dstname && dstname != stdname {
return false, nil
}
この変更により、条件がより厳密になりました。新しい条件は、「レジストリの夏時間名(s
)がAPIの夏時間名(dstname
)と異なる」かつ「APIの夏時間名(dstname
)がAPIの標準時名(stdname
)とも異なる」場合にのみ、マッチングを失敗させる、という意味になります。
この追加された&& dstname != stdname
という部分は非常に重要です。
- もし
dstname == stdname
である場合(つまり、APIが夏時間と標準時の名前を同じと認識している場合、これは通常、そのタイムゾーンがDSTを使用しないことを意味します)、たとえs != dstname
であっても、全体の条件s != dstname && dstname != stdname
は偽になります。これにより、レジストリの夏時間名がAPIの夏時間名と異なっていても、マッチングは続行され、タイムゾーンが正しく特定されるようになります。 - もし
dstname != stdname
である場合(つまり、APIが夏時間と標準時の名前を異なるものと認識している場合、これは通常、そのタイムゾーンがDSTを使用することを示唆します)、そしてs != dstname
であれば、全体の条件は真となり、マッチングは失敗します。これは、DSTを使用するタイムゾーンで、レジストリとAPIの夏時間名が一致しない場合に、誤ったタイムゾーンを特定するのを防ぐための正しい挙動です。
この修正は、Windowsのタイムゾーン情報取得における特定の不整合を考慮に入れ、DSTを使用しない地域でのタイムゾーン特定ロジックをより堅牢にするものです。
関連リンク
- Go言語の
time
パッケージに関する公式ドキュメント: https://pkg.go.dev/time - Windows API
GetTimeZoneInformation
に関するMicrosoftのドキュメント: https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-gettimezoneinformation - Go言語の変更提案(CL): https://golang.org/cl/43210043
参考にした情報源リンク
- 上記のGitHubコミットページ
- Go言語の公式ドキュメント
- Microsoft LearnのWindows APIドキュメント
- コミットメッセージに記載されている情報
- 一般的なタイムゾーンと夏時間に関する知識
- Windowsレジストリにおけるタイムゾーン情報の構造に関する一般的な知識
- Go言語の
time
パッケージのソースコード(特にsrc/pkg/time/zoneinfo_windows.go
)