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 //go:generate go run genzabbrs.go -output zoneinfo_abbrs_windows.go
16 // TODO(rsc): Fall back to copy of zoneinfo files.
18 // BUG(brainman,rsc): On Windows, the operating system does not provide complete
19 // time zone information.
20 // The implementation assumes that this year's rules for daylight savings
21 // time apply to all previous and future years as well.
23 // matchZoneKey checks if stdname and dstname match the corresponding key
24 // values "MUI_Std" and MUI_Dlt" or "Std" and "Dlt" (the latter down-level
25 // from Vista) in the kname key stored under the open registry key zones.
26 func matchZoneKey(zones registry
.Key
, kname
string, stdname
, dstname
string) (matched
bool, err2 error
) {
27 k
, err
:= registry
.OpenKey(zones
, kname
, registry
.READ
)
34 if err
= registry
.LoadRegLoadMUIString(); err
== nil {
35 // Try MUI_Std and MUI_Dlt first, fallback to Std and Dlt if *any* error occurs
36 std
, err
= k
.GetMUIStringValue("MUI_Std")
38 dlt
, err
= k
.GetMUIStringValue("MUI_Dlt")
41 if err
!= nil { // Fallback to Std and Dlt
42 if std
, _
, err
= k
.GetStringValue("Std"); err
!= nil {
45 if dlt
, _
, err
= k
.GetStringValue("Dlt"); err
!= nil {
53 if dlt
!= dstname
&& dstname
!= stdname
{
59 // toEnglishName searches the registry for an English name of a time zone
60 // whose zone names are stdname and dstname and returns the English name.
61 func toEnglishName(stdname
, dstname
string) (string, error
) {
62 k
, err
:= registry
.OpenKey(registry
.LOCAL_MACHINE
, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones`, registry
.ENUMERATE_SUB_KEYS|registry
.QUERY_VALUE
)
68 names
, err
:= k
.ReadSubKeyNames(-1)
72 for _
, name
:= range names
{
73 matched
, err
:= matchZoneKey(k
, name
, stdname
, dstname
)
74 if err
== nil && matched
{
78 return "", errors
.New(`English name for time zone "` + stdname
+ `" not found in registry`)
81 // extractCAPS extracts capital letters from description desc.
82 func extractCAPS(desc
string) string {
84 for _
, c
:= range desc
{
85 if 'A' <= c
&& c
<= 'Z' {
86 short
= append(short
, c
)
92 // abbrev returns the abbreviations to use for the given zone z.
93 func abbrev(z
*syscall
.Timezoneinformation
) (std
, dst
string) {
94 stdName
:= syscall
.UTF16ToString(z
.StandardName
[:])
95 a
, ok
:= abbrs
[stdName
]
97 dstName
:= syscall
.UTF16ToString(z
.DaylightName
[:])
98 // Perhaps stdName is not English. Try to convert it.
99 englishName
, err
:= toEnglishName(stdName
, dstName
)
101 a
, ok
= abbrs
[englishName
]
106 // fallback to using capital letters
107 return extractCAPS(stdName
), extractCAPS(dstName
)
112 // pseudoUnix returns the pseudo-Unix time (seconds since Jan 1 1970 *LOCAL TIME*)
113 // denoted by the system date+time d in the given year.
114 // It is up to the caller to convert this local time into a UTC-based time.
115 func pseudoUnix(year
int, d
*syscall
.Systemtime
) int64 {
116 // Windows specifies daylight savings information in "day in month" format:
117 // d.Month is month number (1-12)
118 // d.DayOfWeek is appropriate weekday (Sunday=0 to Saturday=6)
119 // d.Day is week within the month (1 to 5, where 5 is last week of the month)
120 // d.Hour, d.Minute and d.Second are absolute time
122 t
:= Date(year
, Month(d
.Month
), day
, int(d
.Hour
), int(d
.Minute
), int(d
.Second
), 0, UTC
)
123 i
:= int(d
.DayOfWeek
) - int(t
.Weekday())
128 if week
:= int(d
.Day
) - 1; week
< 4 {
131 // "Last" instance of the day.
133 if day
> daysIn(Month(d
.Month
), year
) {
137 return t
.sec
+ int64(day
-1)*secondsPerDay
+ internalToUnix
140 func initLocalFromTZI(i
*syscall
.Timezoneinformation
) {
146 if i
.StandardDate
.Month
> 0 {
149 l
.zone
= make([]zone
, nzone
)
151 stdname
, dstname
:= abbrev(i
)
156 // No daylight savings.
157 std
.offset
= -int(i
.Bias
) * 60
161 l
.tx
= make([]zoneTrans
, 1)
162 l
.tx
[0].when
= l
.cacheStart
167 // StandardBias must be ignored if StandardDate is not set,
168 // so this computation is delayed until after the nzone==1
170 std
.offset
= -int(i
.Bias
+i
.StandardBias
) * 60
174 dst
.offset
= -int(i
.Bias
+i
.DaylightBias
) * 60
177 // Arrange so that d0 is first transition date, d1 second,
178 // i0 is index of zone after first transition, i1 second.
179 d0
:= &i
.StandardDate
180 d1
:= &i
.DaylightDate
183 if d0
.Month
> d1
.Month
{
188 // 2 tx per year, 100 years on each side of this year
189 l
.tx
= make([]zoneTrans
, 400)
194 for y
:= year
- 100; y
< year
+100; y
++ {
196 tx
.when
= pseudoUnix(y
, d0
) - int64(l
.zone
[i1
].offset
)
201 tx
.when
= pseudoUnix(y
, d1
) - int64(l
.zone
[i0
].offset
)
207 var usPacific
= syscall
.Timezoneinformation
{
209 StandardName
: [32]uint16{
210 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e',
212 StandardDate
: syscall
.Systemtime
{Month
: 11, Day
: 1, Hour
: 2},
213 DaylightName
: [32]uint16{
214 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e',
216 DaylightDate
: syscall
.Systemtime
{Month
: 3, Day
: 2, Hour
: 2},
220 var aus
= syscall
.Timezoneinformation
{
222 StandardName
: [32]uint16{
223 'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e',
225 StandardDate
: syscall
.Systemtime
{Month
: 4, Day
: 1, Hour
: 3},
226 DaylightName
: [32]uint16{
227 'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e',
229 DaylightDate
: syscall
.Systemtime
{Month
: 10, Day
: 1, Hour
: 2},
233 func initTestingZone() {
234 initLocalFromTZI(&usPacific
)
237 func initAusTestingZone() {
238 initLocalFromTZI(&aus
)
242 var i syscall
.Timezoneinformation
243 if _
, err
:= syscall
.GetTimeZoneInformation(&i
); err
!= nil {
244 localLoc
.name
= "UTC"
250 func loadLocation(name
string) (*Location
, error
) {
251 z
, err
:= loadZoneFile(runtime
.GOROOT()+`\lib\time\zoneinfo.zip`, name
)
259 func forceZipFileForTesting(zipOnly
bool) {
260 // We only use the zip file anyway.