1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
9 "internal/syscall/windows/registry"
14 var zoneSources
= []string{
15 runtime
.GOROOT() + "/lib/time/zoneinfo.zip",
18 // TODO(rsc): Fall back to copy of zoneinfo files.
20 // BUG(brainman,rsc): On Windows, the operating system does not provide complete
21 // time zone information.
22 // The implementation assumes that this year's rules for daylight savings
23 // time apply to all previous and future years as well.
25 // matchZoneKey checks if stdname and dstname match the corresponding key
26 // values "MUI_Std" and MUI_Dlt" or "Std" and "Dlt" (the latter down-level
27 // from Vista) in the kname key stored under the open registry key zones.
28 func matchZoneKey(zones registry
.Key
, kname
string, stdname
, dstname
string) (matched
bool, err2 error
) {
29 k
, err
:= registry
.OpenKey(zones
, kname
, registry
.READ
)
36 if err
= registry
.LoadRegLoadMUIString(); err
== nil {
37 // Try MUI_Std and MUI_Dlt first, fallback to Std and Dlt if *any* error occurs
38 std
, err
= k
.GetMUIStringValue("MUI_Std")
40 dlt
, err
= k
.GetMUIStringValue("MUI_Dlt")
43 if err
!= nil { // Fallback to Std and Dlt
44 if std
, _
, err
= k
.GetStringValue("Std"); err
!= nil {
47 if dlt
, _
, err
= k
.GetStringValue("Dlt"); err
!= nil {
55 if dlt
!= dstname
&& dstname
!= stdname
{
61 // toEnglishName searches the registry for an English name of a time zone
62 // whose zone names are stdname and dstname and returns the English name.
63 func toEnglishName(stdname
, dstname
string) (string, error
) {
64 k
, err
:= registry
.OpenKey(registry
.LOCAL_MACHINE
, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones`, registry
.ENUMERATE_SUB_KEYS|registry
.QUERY_VALUE
)
70 names
, err
:= k
.ReadSubKeyNames(-1)
74 for _
, name
:= range names
{
75 matched
, err
:= matchZoneKey(k
, name
, stdname
, dstname
)
76 if err
== nil && matched
{
80 return "", errors
.New(`English name for time zone "` + stdname
+ `" not found in registry`)
83 // extractCAPS extracts capital letters from description desc.
84 func extractCAPS(desc
string) string {
86 for _
, c
:= range desc
{
87 if 'A' <= c
&& c
<= 'Z' {
88 short
= append(short
, c
)
94 // abbrev returns the abbreviations to use for the given zone z.
95 func abbrev(z
*syscall
.Timezoneinformation
) (std
, dst
string) {
96 stdName
:= syscall
.UTF16ToString(z
.StandardName
[:])
97 a
, ok
:= abbrs
[stdName
]
99 dstName
:= syscall
.UTF16ToString(z
.DaylightName
[:])
100 // Perhaps stdName is not English. Try to convert it.
101 englishName
, err
:= toEnglishName(stdName
, dstName
)
103 a
, ok
= abbrs
[englishName
]
108 // fallback to using capital letters
109 return extractCAPS(stdName
), extractCAPS(dstName
)
114 // pseudoUnix returns the pseudo-Unix time (seconds since Jan 1 1970 *LOCAL TIME*)
115 // denoted by the system date+time d in the given year.
116 // It is up to the caller to convert this local time into a UTC-based time.
117 func pseudoUnix(year
int, d
*syscall
.Systemtime
) int64 {
118 // Windows specifies daylight savings information in "day in month" format:
119 // d.Month is month number (1-12)
120 // d.DayOfWeek is appropriate weekday (Sunday=0 to Saturday=6)
121 // d.Day is week within the month (1 to 5, where 5 is last week of the month)
122 // d.Hour, d.Minute and d.Second are absolute time
124 t
:= Date(year
, Month(d
.Month
), day
, int(d
.Hour
), int(d
.Minute
), int(d
.Second
), 0, UTC
)
125 i
:= int(d
.DayOfWeek
) - int(t
.Weekday())
130 if week
:= int(d
.Day
) - 1; week
< 4 {
133 // "Last" instance of the day.
135 if day
> daysIn(Month(d
.Month
), year
) {
139 return t
.sec() + int64(day
-1)*secondsPerDay
+ internalToUnix
142 func initLocalFromTZI(i
*syscall
.Timezoneinformation
) {
148 if i
.StandardDate
.Month
> 0 {
151 l
.zone
= make([]zone
, nzone
)
153 stdname
, dstname
:= abbrev(i
)
158 // No daylight savings.
159 std
.offset
= -int(i
.Bias
) * 60
163 l
.tx
= make([]zoneTrans
, 1)
164 l
.tx
[0].when
= l
.cacheStart
169 // StandardBias must be ignored if StandardDate is not set,
170 // so this computation is delayed until after the nzone==1
172 std
.offset
= -int(i
.Bias
+i
.StandardBias
) * 60
176 dst
.offset
= -int(i
.Bias
+i
.DaylightBias
) * 60
179 // Arrange so that d0 is first transition date, d1 second,
180 // i0 is index of zone after first transition, i1 second.
181 d0
:= &i
.StandardDate
182 d1
:= &i
.DaylightDate
185 if d0
.Month
> d1
.Month
{
190 // 2 tx per year, 100 years on each side of this year
191 l
.tx
= make([]zoneTrans
, 400)
196 for y
:= year
- 100; y
< year
+100; y
++ {
198 tx
.when
= pseudoUnix(y
, d0
) - int64(l
.zone
[i1
].offset
)
203 tx
.when
= pseudoUnix(y
, d1
) - int64(l
.zone
[i0
].offset
)
209 var usPacific
= syscall
.Timezoneinformation
{
211 StandardName
: [32]uint16{
212 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e',
214 StandardDate
: syscall
.Systemtime
{Month
: 11, Day
: 1, Hour
: 2},
215 DaylightName
: [32]uint16{
216 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e',
218 DaylightDate
: syscall
.Systemtime
{Month
: 3, Day
: 2, Hour
: 2},
222 var aus
= syscall
.Timezoneinformation
{
224 StandardName
: [32]uint16{
225 'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e',
227 StandardDate
: syscall
.Systemtime
{Month
: 4, Day
: 1, Hour
: 3},
228 DaylightName
: [32]uint16{
229 'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e',
231 DaylightDate
: syscall
.Systemtime
{Month
: 10, Day
: 1, Hour
: 2},
236 var i syscall
.Timezoneinformation
237 if _
, err
:= syscall
.GetTimeZoneInformation(&i
); err
!= nil {
238 localLoc
.name
= "UTC"