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 // TODO(rsc): Fall back to copy of zoneinfo files.
16 // BUG(brainman,rsc): On Windows, the operating system does not provide complete
17 // time zone information.
18 // The implementation assumes that this year's rules for daylight savings
19 // time apply to all previous and future years as well.
21 // matchZoneKey checks if stdname and dstname match the corresponding key
22 // values "MUI_Std" and MUI_Dlt" or "Std" and "Dlt" (the latter down-level
23 // from Vista) in the kname key stored under the open registry key zones.
24 func matchZoneKey(zones registry
.Key
, kname
string, stdname
, dstname
string) (matched
bool, err2 error
) {
25 k
, err
:= registry
.OpenKey(zones
, kname
, registry
.READ
)
32 if err
= registry
.LoadRegLoadMUIString(); err
== nil {
33 // Try MUI_Std and MUI_Dlt first, fallback to Std and Dlt if *any* error occurs
34 std
, err
= k
.GetMUIStringValue("MUI_Std")
36 dlt
, err
= k
.GetMUIStringValue("MUI_Dlt")
39 if err
!= nil { // Fallback to Std and Dlt
40 if std
, _
, err
= k
.GetStringValue("Std"); err
!= nil {
43 if dlt
, _
, err
= k
.GetStringValue("Dlt"); err
!= nil {
51 if dlt
!= dstname
&& dstname
!= stdname
{
57 // toEnglishName searches the registry for an English name of a time zone
58 // whose zone names are stdname and dstname and returns the English name.
59 func toEnglishName(stdname
, dstname
string) (string, error
) {
60 k
, err
:= registry
.OpenKey(registry
.LOCAL_MACHINE
, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones`, registry
.ENUMERATE_SUB_KEYS|registry
.QUERY_VALUE
)
66 names
, err
:= k
.ReadSubKeyNames(-1)
70 for _
, name
:= range names
{
71 matched
, err
:= matchZoneKey(k
, name
, stdname
, dstname
)
72 if err
== nil && matched
{
76 return "", errors
.New(`English name for time zone "` + stdname
+ `" not found in registry`)
79 // extractCAPS extracts capital letters from description desc.
80 func extractCAPS(desc
string) string {
82 for _
, c
:= range desc
{
83 if 'A' <= c
&& c
<= 'Z' {
84 short
= append(short
, c
)
90 // abbrev returns the abbreviations to use for the given zone z.
91 func abbrev(z
*syscall
.Timezoneinformation
) (std
, dst
string) {
92 stdName
:= syscall
.UTF16ToString(z
.StandardName
[:])
93 a
, ok
:= abbrs
[stdName
]
95 dstName
:= syscall
.UTF16ToString(z
.DaylightName
[:])
96 // Perhaps stdName is not English. Try to convert it.
97 englishName
, err
:= toEnglishName(stdName
, dstName
)
99 a
, ok
= abbrs
[englishName
]
104 // fallback to using capital letters
105 return extractCAPS(stdName
), extractCAPS(dstName
)
110 // pseudoUnix returns the pseudo-Unix time (seconds since Jan 1 1970 *LOCAL TIME*)
111 // denoted by the system date+time d in the given year.
112 // It is up to the caller to convert this local time into a UTC-based time.
113 func pseudoUnix(year
int, d
*syscall
.Systemtime
) int64 {
114 // Windows specifies daylight savings information in "day in month" format:
115 // d.Month is month number (1-12)
116 // d.DayOfWeek is appropriate weekday (Sunday=0 to Saturday=6)
117 // d.Day is week within the month (1 to 5, where 5 is last week of the month)
118 // d.Hour, d.Minute and d.Second are absolute time
120 t
:= Date(year
, Month(d
.Month
), day
, int(d
.Hour
), int(d
.Minute
), int(d
.Second
), 0, UTC
)
121 i
:= int(d
.DayOfWeek
) - int(t
.Weekday())
126 if week
:= int(d
.Day
) - 1; week
< 4 {
129 // "Last" instance of the day.
131 if day
> daysIn(Month(d
.Month
), year
) {
135 return t
.sec() + int64(day
-1)*secondsPerDay
+ internalToUnix
138 func initLocalFromTZI(i
*syscall
.Timezoneinformation
) {
144 if i
.StandardDate
.Month
> 0 {
147 l
.zone
= make([]zone
, nzone
)
149 stdname
, dstname
:= abbrev(i
)
154 // No daylight savings.
155 std
.offset
= -int(i
.Bias
) * 60
159 l
.tx
= make([]zoneTrans
, 1)
160 l
.tx
[0].when
= l
.cacheStart
165 // StandardBias must be ignored if StandardDate is not set,
166 // so this computation is delayed until after the nzone==1
168 std
.offset
= -int(i
.Bias
+i
.StandardBias
) * 60
172 dst
.offset
= -int(i
.Bias
+i
.DaylightBias
) * 60
175 // Arrange so that d0 is first transition date, d1 second,
176 // i0 is index of zone after first transition, i1 second.
177 d0
:= &i
.StandardDate
178 d1
:= &i
.DaylightDate
181 if d0
.Month
> d1
.Month
{
186 // 2 tx per year, 100 years on each side of this year
187 l
.tx
= make([]zoneTrans
, 400)
192 for y
:= year
- 100; y
< year
+100; y
++ {
194 tx
.when
= pseudoUnix(y
, d0
) - int64(l
.zone
[i1
].offset
)
199 tx
.when
= pseudoUnix(y
, d1
) - int64(l
.zone
[i0
].offset
)
205 var usPacific
= syscall
.Timezoneinformation
{
207 StandardName
: [32]uint16{
208 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e',
210 StandardDate
: syscall
.Systemtime
{Month
: 11, Day
: 1, Hour
: 2},
211 DaylightName
: [32]uint16{
212 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e',
214 DaylightDate
: syscall
.Systemtime
{Month
: 3, Day
: 2, Hour
: 2},
218 var aus
= syscall
.Timezoneinformation
{
220 StandardName
: [32]uint16{
221 'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e',
223 StandardDate
: syscall
.Systemtime
{Month
: 4, Day
: 1, Hour
: 3},
224 DaylightName
: [32]uint16{
225 'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e',
227 DaylightDate
: syscall
.Systemtime
{Month
: 10, Day
: 1, Hour
: 2},
231 func initTestingZone() {
232 initLocalFromTZI(&usPacific
)
235 func initAusTestingZone() {
236 initLocalFromTZI(&aus
)
240 var i syscall
.Timezoneinformation
241 if _
, err
:= syscall
.GetTimeZoneInformation(&i
); err
!= nil {
242 localLoc
.name
= "UTC"
248 func loadLocation(name
string) (*Location
, error
) {
249 z
, err
:= loadZoneFile(runtime
.GOROOT()+`\lib\time\zoneinfo.zip`, name
)
257 func forceZipFileForTesting(zipOnly
bool) {
258 // We only use the zip file anyway.