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

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

このコミットは、Go言語の標準ライブラリであるtimeパッケージにおける、Windows環境でのタイムゾーン情報(特に夏時間:DST)の取り扱いに関するバグ修正です。具体的には、夏時間(DST)が設定されていないタイムゾーンのローカル時間計算が正しく行われない問題を解決しています。

コミット

commit d3c92b7c903064f31c6f0aec4c3be5cfd30b0e53
Author: Alex Brainman <alex.brainman@gmail.com>
Date:   Tue Apr 3 11:39:38 2012 +1000

    time: fix handling of locations with no dst on windows
    
    Fixes #3437.
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/5967063

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

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

元コミット内容

time: fix handling of locations with no dst on windows

Fixes #3437.

R=rsc
CC=golang-dev
https://golang.org/cl/5967063

変更の背景

このコミットは、Go言語のtimeパッケージがWindows上で夏時間(Daylight Saving Time, DST)を持たないタイムゾーンを扱う際に発生していたバグを修正するために行われました。具体的には、Issue #3437で報告された問題に対応しています。

Goのtimeパッケージは、OSが提供するタイムゾーン情報を使用してローカル時間を計算します。Windowsシステムでは、タイムゾーン情報はTIME_ZONE_INFORMATION構造体(またはDYNAMIC_TIME_ZONE_INFORMATION)によって管理され、夏時間の開始・終了規則などが含まれています。しかし、夏時間を持たないタイムゾーン(例:UTC、一部の地域)の場合、これらの規則は適用されません。

問題は、timeパッケージが夏時間を持たないタイムゾーンを初期化する際に、内部的なタイムゾーン遷移情報(zoneTrans)が適切に設定されていなかったことに起因します。これにより、夏時間のないタイムゾーンであっても、誤った時間計算が行われる可能性がありました。特に、time.LoadLocationなどでタイムゾーンをロードした際に、内部状態が不完全なままとなり、その後の時間計算で予期せぬ結果を招くことが考えられます。

この修正は、夏時間のないタイムゾーンが正しく初期化され、常に標準時として扱われるようにすることで、この問題を解決します。

前提知識の解説

タイムゾーンと夏時間 (DST)

  • タイムゾーン: 地球上の特定の地域で共通して使用される標準時を定義する領域です。UTC(協定世界時)からのオフセット(例: UTC+9)で表されます。
  • 夏時間 (Daylight Saving Time, DST): 特定の期間(通常は夏の間)に時間を1時間進める制度です。これにより、日中の明るい時間を有効活用し、エネルギー消費を抑えるなどの目的があります。夏時間が導入されている地域では、年に2回(開始時と終了時)時刻が変更されます。

Go言語のtimeパッケージ

Go言語のtimeパッケージは、時刻の表現、時間間隔の計算、タイムゾーンの処理など、時間に関する機能を提供します。

  • time.Time構造体: 特定の時点を表します。
  • time.Location構造体: タイムゾーン情報を表します。time.LoadLocation関数を使って、名前(例: "America/New_York")やシステム情報からタイムゾーンをロードできます。
  • 内部的には、time.Locationはタイムゾーンのオフセット、略称、そして夏時間による遷移情報(zoneTrans)を保持しています。

Windowsのタイムゾーン情報

Windowsオペレーティングシステムは、レジストリにタイムゾーン情報を格納しており、Win32 APIを通じてアクセスできます。

  • GetTimeZoneInformation関数: システムの現在のタイムゾーン情報を取得します。
  • TIME_ZONE_INFORMATION構造体: タイムゾーンの標準時と夏時間のオフセット、名称、夏時間の開始・終了日時などの情報を含みます。夏時間がない場合は、夏時間関連のフィールドがゼロまたは無効な値になります。

zoneTranszoneTransCache

Goのtimeパッケージ内部では、タイムゾーンの遷移(夏時間の開始・終了など)を効率的に処理するために、zoneTransという構造体とzoneTransCacheというキャッシュ機構を使用しています。

  • zoneTrans: 特定の時刻(when)におけるタイムゾーンのオフセットや略称などの情報(index)を保持します。夏時間のあるタイムゾーンでは、複数のzoneTransエントリが存在し、時間の経過とともに適用されるルールが切り替わります。
  • zoneTransCache: タイムゾーンの計算を高速化するためのキャッシュです。cacheStartcacheEndcacheZoneなどのフィールドを持ち、特定の時間範囲におけるタイムゾーン情報を保持します。

夏時間のないタイムゾーンの場合、タイムゾーンの遷移は発生しないため、zoneTransは1つのエントリのみを持つべきです。このエントリは、そのタイムゾーンの標準時を表します。

技術的詳細

このコミットの技術的詳細は、Go言語のtimeパッケージがWindowsのタイムゾーン情報を初期化する際のロジックにあります。

src/pkg/time/zoneinfo_windows.goファイルは、Windows固有のタイムゾーン情報処理を担当しています。initLocalFromTZI関数は、Windows APIから取得したsyscall.Timezoneinformation構造体(これはTIME_ZONE_INFORMATION構造体に対応します)を基に、Goのtime.Location構造体を初期化します。

夏時間がないタイムゾーンの場合、initLocalFromTZI関数内の特定の条件分岐に入ります。この条件分岐は、i.DaylightDate.Month == 0(夏時間の開始月が0、つまり夏時間がないことを示す)の場合に実行されます。

修正前のコードでは、この夏時間がない場合の処理において、l.txzoneTransのスライス)が初期化されていませんでした。l.txはタイムゾーンの遷移情報を保持する重要なフィールドであり、夏時間がない場合でも、少なくとも標準時を表す1つのエントリが必要です。

このコミットでは、夏時間がないタイムゾーンの初期化パスに以下の3行が追加されました。

l.tx = make([]zoneTrans, 1)
l.tx[0].when = l.cacheStart
l.tx[0].index = 0

これにより、以下の点が保証されます。

  1. l.txの初期化: l.txzoneTrans型のスライスとして適切に初期化され、1つの要素を保持するようになります。
  2. 単一の遷移エントリ: l.tx[0]に、タイムゾーンの開始時刻(l.cacheStart、通常はGoの時間の最小値)から適用される単一のタイムゾーン情報が設定されます。
  3. 標準時インデックス: l.tx[0].index = 0は、このエントリが標準時(std)に対応するタイムゾーン情報を使用することを示します。stdは、initLocalFromTZI関数内で定義される、標準時のオフセットと略称を持つzone構造体です。

この修正により、夏時間のないタイムゾーンがロードされた際に、time.Locationオブジェクトが完全な内部状態を持つようになり、その後の時間計算が正しく行われるようになります。特に、time.TimeオブジェクトのInメソッドなど、タイムゾーン変換を行う関数が、夏時間のないタイムゾーンに対しても期待通りに動作するようになります。

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

変更はsrc/pkg/time/zoneinfo_windows.goファイル内のinitLocalFromTZI関数に集中しています。

--- a/src/pkg/time/zoneinfo_windows.go
+++ b/src/pkg/time/zoneinfo_windows.go
@@ -83,6 +83,9 @@ func initLocalFromTZI(i *syscall.Timezoneinformation) {
 		l.cacheStart = -1 << 63
 		l.cacheEnd = 1<<63 - 1
 		l.cacheZone = std
+		l.tx = make([]zoneTrans, 1)
+		l.tx[0].when = l.cacheStart
+		l.tx[0].index = 0
 		return
 	}
 

コアとなるコードの解説

上記のコードスニペットは、initLocalFromTZI関数内で、夏時間(DST)が設定されていないタイムゾーンを処理する部分です。

  • if i.DaylightDate.Month == 0 { ... }: この条件は、Windowsのタイムゾーン情報(i)において、夏時間の開始月が0である場合に真となります。これは、そのタイムゾーンが夏時間を持たないことを意味します。
  • l.cacheStart = -1 << 63: ltime.Location構造体です。cacheStartは、このタイムゾーン情報が有効な開始時刻を示します。-1 << 63はGoのint64型の最小値であり、事実上「常に有効」であることを意味します。
  • l.cacheEnd = 1<<63 - 1: cacheEndは有効な終了時刻を示し、1<<63 - 1int64型の最大値であり、これも「常に有効」であることを意味します。
  • l.cacheZone = std: cacheZoneは、このキャッシュ期間中に適用されるタイムゾーン情報(オフセット、略称など)を指します。stdは、この関数内で定義されている標準時(Standard Time)のzone構造体です。
  • 追加された行:
    • l.tx = make([]zoneTrans, 1): l.txtime.Location構造体のフィールドで、zoneTrans型のスライスです。これは、タイムゾーンの遷移(夏時間の開始・終了など)を記録するために使用されます。夏時間がない場合でも、少なくとも標準時を表す1つのエントリが必要です。この行は、そのためのスライスを初期化し、1つの要素を保持するようにします。
    • l.tx[0].when = l.cacheStart: スライスの最初の要素(インデックス0)のwhenフィールドに、このタイムゾーン情報が適用される開始時刻を設定します。l.cacheStartと同じく、事実上「常に」適用されることを意味します。
    • l.tx[0].index = 0: スライスの最初の要素のindexフィールドに0を設定します。これは、このzoneTransエントリが、time.Location構造体内のzoneスライス(l.zone)のインデックス0にあるタイムゾーン情報(この場合はstd、つまり標準時)を参照することを示します。

これらの追加により、夏時間を持たないタイムゾーンがGoのtimeパッケージにロードされた際に、そのtime.Locationオブジェクトが内部的に完全な状態となり、時間計算が正しく行われるようになります。

関連リンク

参考にした情報源リンク

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

このコミットは、Go言語の標準ライブラリであるtimeパッケージにおける、Windows環境でのタイムゾーン情報(特に夏時間:DST)の取り扱いに関するバグ修正です。具体的には、夏時間(DST)が設定されていないタイムゾーンのローカル時間計算が正しく行われない問題を解決しています。

コミット

commit d3c92b7c903064f31c6f0aec4c3be5cfd30b0e53
Author: Alex Brainman <alex.brainman@gmail.com>
Date:   Tue Apr 3 11:39:38 2012 +1000

    time: fix handling of locations with no dst on windows
    
    Fixes #3437.
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/5967063

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

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

元コミット内容

time: fix handling of locations with no dst on windows

Fixes #3437.

R=rsc
CC=golang-dev
https://golang.org/cl/5967063

変更の背景

このコミットは、Go言語のtimeパッケージがWindows上で夏時間(Daylight Saving Time, DST)を持たないタイムゾーンを扱う際に発生していたバグを修正するために行われました。具体的には、Issue #3437で報告された問題に対応しています。

Goのtimeパッケージは、OSが提供するタイムゾーン情報を使用してローカル時間を計算します。Windowsシステムでは、タイムゾーン情報はTIME_ZONE_INFORMATION構造体(またはDYNAMIC_TIME_ZONE_INFORMATION)によって管理され、夏時間の開始・終了規則などが含まれています。しかし、夏時間を持たないタイムゾーン(例:UTC、一部の地域)の場合、これらの規則は適用されません。

問題は、timeパッケージが夏時間を持たないタイムゾーンを初期化する際に、内部的なタイムゾーン遷移情報(zoneTrans)が適切に設定されていなかったことに起因します。これにより、夏時間のないタイムゾーンであっても、誤った時間計算が行われる可能性がありました。特に、time.LoadLocationなどでタイムゾーンをロードした際に、内部状態が不完全なままとなり、その後の時間計算で予期せぬ結果を招くことが考えられます。

この修正は、夏時間のないタイムゾーンが正しく初期化され、常に標準時として扱われるようにすることで、この問題を解決します。

前提知識の解説

タイムゾーンと夏時間 (DST)

  • タイムゾーン: 地球上の特定の地域で共通して使用される標準時を定義する領域です。UTC(協定世界時)からのオフセット(例: UTC+9)で表されます。
  • 夏時間 (Daylight Saving Time, DST): 特定の期間(通常は夏の間)に時間を1時間進める制度です。これにより、日中の明るい時間を有効活用し、エネルギー消費を抑えるなどの目的があります。夏時間が導入されている地域では、年に2回(開始時と終了時)時刻が変更されます。

Go言語のtimeパッケージ

Go言語のtimeパッケージは、時刻の表現、時間間隔の計算、タイムゾーンの処理など、時間に関する機能を提供します。

  • time.Time構造体: 特定の時点を表します。
  • time.Location構造体: タイムゾーン情報を表します。time.LoadLocation関数を使って、名前(例: "America/New_York")やシステム情報からタイムゾーンをロードできます。
  • 内部的には、time.Locationはタイムゾーンのオフセット、略称、そして夏時間による遷移情報(zoneTrans)を保持しています。

Windowsのタイムゾーン情報

Windowsオペレーティングシステムは、レジストリにタイムゾーン情報を格納しており、Win32 APIを通じてアクセスできます。

  • GetTimeZoneInformation関数: システムの現在のタイムゾーン情報を取得します。
  • TIME_ZONE_INFORMATION構造体: タイムゾーンの標準時と夏時間のオフセット、名称、夏時間の開始・終了日時などの情報を含みます。夏時間がない場合は、夏時間関連のフィールドがゼロまたは無効な値になります。

zoneTranszoneTransCache

Goのtimeパッケージ内部では、タイムゾーンの遷移(夏時間の開始・終了など)を効率的に処理するために、zoneTransという構造体とzoneTransCacheというキャッシュ機構を使用しています。

  • zoneTrans: 特定の時刻(when)におけるタイムゾーンのオフセットや略称などの情報(index)を保持します。夏時間のあるタイムゾーンでは、複数のzoneTransエントリが存在し、時間の経過とともに適用されるルールが切り替わります。
  • zoneTransCache: タイムゾーンの計算を高速化するためのキャッシュです。cacheStartcacheEndcacheZoneなどのフィールドを持ち、特定の時間範囲におけるタイムゾーン情報を保持します。

夏時間のないタイムゾーンの場合、タイムゾーンの遷移は発生しないため、zoneTransは1つのエントリのみを持つべきです。このエントリは、そのタイムゾーンの標準時を表します。

技術的詳細

このコミットの技術的詳細は、Go言語のtimeパッケージがWindowsのタイムゾーン情報を初期化する際のロジックにあります。

src/pkg/time/zoneinfo_windows.goファイルは、Windows固有のタイムゾーン情報処理を担当しています。initLocalFromTZI関数は、Windows APIから取得したsyscall.Timezoneinformation構造体(これはTIME_ZONE_INFORMATION構造体に対応します)を基に、Goのtime.Location構造体を初期化します。

夏時間がないタイムゾーンの場合、initLocalFromTZI関数内の特定の条件分岐に入ります。この条件分岐は、i.DaylightDate.Month == 0(夏時間の開始月が0、つまり夏時間がないことを示す)の場合に実行されます。

修正前のコードでは、この夏時間がない場合の処理において、l.txzoneTransのスライス)が初期化されていませんでした。l.txはタイムゾーンの遷移情報を保持する重要なフィールドであり、夏時間がない場合でも、少なくとも標準時を表す1つのエントリが必要です。

このコミットでは、夏時間がないタイムゾーンの初期化パスに以下の3行が追加されました。

l.tx = make([]zoneTrans, 1)
l.tx[0].when = l.cacheStart
l.tx[0].index = 0

これにより、以下の点が保証されます。

  1. l.txの初期化: l.txzoneTrans型のスライスとして適切に初期化され、1つの要素を保持するようになります。
  2. 単一の遷移エントリ: l.tx[0]に、タイムゾーンの開始時刻(l.cacheStart、通常はGoの時間の最小値)から適用される単一のタイムゾーン情報が設定されます。
  3. 標準時インデックス: l.tx[0].index = 0は、このエントリが標準時(std)に対応するタイムゾーン情報を使用することを示します。stdは、initLocalFromTZI関数内で定義される、標準時のオフセットと略称を持つzone構造体です。

この修正により、夏時間のないタイムゾーンがロードされた際に、time.Locationオブジェクトが完全な内部状態を持つようになり、その後の時間計算が正しく行われるようになります。特に、time.TimeオブジェクトのInメソッドなど、タイムゾーン変換を行う関数が、夏時間のないタイムゾーンに対しても期待通りに動作するようになります。

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

変更はsrc/pkg/time/zoneinfo_windows.goファイル内のinitLocalFromTZI関数に集中しています。

--- a/src/pkg/time/zoneinfo_windows.go
+++ b/src/pkg/time/zoneinfo_windows.go
@@ -83,6 +83,9 @@ func initLocalFromTZI(i *syscall.Timezoneinformation) {
 		l.cacheStart = -1 << 63
 		l.cacheEnd = 1<<63 - 1
 		l.cacheZone = std
+		l.tx = make([]zoneTrans, 1)
+		l.tx[0].when = l.cacheStart
+		l.tx[0].index = 0
 		return
 	}
 

コアとなるコードの解説

上記のコードスニペットは、initLocalFromTZI関数内で、夏時間(DST)が設定されていないタイムゾーンを処理する部分です。

  • if i.DaylightDate.Month == 0 { ... }: この条件は、Windowsのタイムゾーン情報(i)において、夏時間の開始月が0である場合に真となります。これは、そのタイムゾーンが夏時間を持たないことを意味します。
  • l.cacheStart = -1 << 63: ltime.Location構造体です。cacheStartは、このタイムゾーン情報が有効な開始時刻を示します。-1 << 63はGoのint64型の最小値であり、事実上「常に有効」であることを意味します。
  • l.cacheEnd = 1<<63 - 1: cacheEndは有効な終了時刻を示し、1<<63 - 1int64型の最大値であり、これも「常に有効」であることを意味します。
  • l.cacheZone = std: cacheZoneは、このキャッシュ期間中に適用されるタイムゾーン情報(オフセット、略称など)を指します。stdは、この関数内で定義されている標準時(Standard Time)のzone構造体です。
  • 追加された行:
    • l.tx = make([]zoneTrans, 1): l.txtime.Location構造体のフィールドで、zoneTrans型のスライスです。これは、タイムゾーンの遷移(夏時間の開始・終了など)を記録するために使用されます。夏時間がない場合でも、少なくとも標準時を表す1つのエントリが必要です。この行は、そのためのスライスを初期化し、1つの要素を保持するようにします。
    • l.tx[0].when = l.cacheStart: スライスの最初の要素(インデックス0)のwhenフィールドに、このタイムゾーン情報が適用される開始時刻を設定します。l.cacheStartと同じく、事実上「常に」適用されることを意味します。
    • l.tx[0].index = 0: スライスの最初の要素のindexフィールドに0を設定します。これは、このzoneTransエントリが、time.Location構造体内のzoneスライス(l.zone)のインデックス0にあるタイムゾーン情報(この場合はstd、つまり標準時)を参照することを示します。

これらの追加により、夏時間を持たないタイムゾーンがGoのtimeパッケージにロードされた際に、そのtime.Locationオブジェクトが内部的に完全な状態となり、時間計算が正しく行われるようになります。

関連リンク

参考にした情報源リンク