2016-08-05 Vladimir Makarov <vmakarov@redhat.com>
[official-gcc.git] / libgo / go / time / zoneinfo_windows.go
bloba6546f54b86ac79cb57f8dcee1e2885665e78644
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.
5 package time
7 import (
8 "errors"
9 "internal/syscall/windows/registry"
10 "runtime"
11 "syscall"
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)
28 if err != nil {
29 return false, err
31 defer k.Close()
33 var std, dlt string
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")
37 if err == nil {
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 {
43 return false, err
45 if dlt, _, err = k.GetStringValue("Dlt"); err != nil {
46 return false, err
50 if std != stdname {
51 return false, nil
53 if dlt != dstname && dstname != stdname {
54 return false, nil
56 return true, nil
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)
63 if err != nil {
64 return "", err
66 defer k.Close()
68 names, err := k.ReadSubKeyNames(-1)
69 if err != nil {
70 return "", err
72 for _, name := range names {
73 matched, err := matchZoneKey(k, name, stdname, dstname)
74 if err == nil && matched {
75 return name, nil
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 {
83 var short []rune
84 for _, c := range desc {
85 if 'A' <= c && c <= 'Z' {
86 short = append(short, c)
89 return string(short)
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]
96 if !ok {
97 dstName := syscall.UTF16ToString(z.DaylightName[:])
98 // Perhaps stdName is not English. Try to convert it.
99 englishName, err := toEnglishName(stdName, dstName)
100 if err == nil {
101 a, ok = abbrs[englishName]
102 if ok {
103 return a.std, a.dst
106 // fallback to using capital letters
107 return extractCAPS(stdName), extractCAPS(dstName)
109 return a.std, a.dst
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
121 day := 1
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())
124 if i < 0 {
125 i += 7
127 day += i
128 if week := int(d.Day) - 1; week < 4 {
129 day += week * 7
130 } else {
131 // "Last" instance of the day.
132 day += 4 * 7
133 if day > daysIn(Month(d.Month), year) {
134 day -= 7
137 return t.sec + int64(day-1)*secondsPerDay + internalToUnix
140 func initLocalFromTZI(i *syscall.Timezoneinformation) {
141 l := &localLoc
143 l.name = "Local"
145 nzone := 1
146 if i.StandardDate.Month > 0 {
147 nzone++
149 l.zone = make([]zone, nzone)
151 stdname, dstname := abbrev(i)
153 std := &l.zone[0]
154 std.name = stdname
155 if nzone == 1 {
156 // No daylight savings.
157 std.offset = -int(i.Bias) * 60
158 l.cacheStart = alpha
159 l.cacheEnd = omega
160 l.cacheZone = std
161 l.tx = make([]zoneTrans, 1)
162 l.tx[0].when = l.cacheStart
163 l.tx[0].index = 0
164 return
167 // StandardBias must be ignored if StandardDate is not set,
168 // so this computation is delayed until after the nzone==1
169 // return above.
170 std.offset = -int(i.Bias+i.StandardBias) * 60
172 dst := &l.zone[1]
173 dst.name = dstname
174 dst.offset = -int(i.Bias+i.DaylightBias) * 60
175 dst.isDST = true
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
181 i0 := 0
182 i1 := 1
183 if d0.Month > d1.Month {
184 d0, d1 = d1, d0
185 i0, i1 = i1, i0
188 // 2 tx per year, 100 years on each side of this year
189 l.tx = make([]zoneTrans, 400)
191 t := Now().UTC()
192 year := t.Year()
193 txi := 0
194 for y := year - 100; y < year+100; y++ {
195 tx := &l.tx[txi]
196 tx.when = pseudoUnix(y, d0) - int64(l.zone[i1].offset)
197 tx.index = uint8(i0)
198 txi++
200 tx = &l.tx[txi]
201 tx.when = pseudoUnix(y, d1) - int64(l.zone[i0].offset)
202 tx.index = uint8(i1)
203 txi++
207 var usPacific = syscall.Timezoneinformation{
208 Bias: 8 * 60,
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},
217 DaylightBias: -60,
220 var aus = syscall.Timezoneinformation{
221 Bias: -10 * 60,
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},
230 DaylightBias: -60,
233 func initTestingZone() {
234 initLocalFromTZI(&usPacific)
237 func initAusTestingZone() {
238 initLocalFromTZI(&aus)
241 func initLocal() {
242 var i syscall.Timezoneinformation
243 if _, err := syscall.GetTimeZoneInformation(&i); err != nil {
244 localLoc.name = "UTC"
245 return
247 initLocalFromTZI(&i)
250 func loadLocation(name string) (*Location, error) {
251 z, err := loadZoneFile(runtime.GOROOT()+`\lib\time\zoneinfo.zip`, name)
252 if err != nil {
253 return nil, err
255 z.name = name
256 return z, nil
259 func forceZipFileForTesting(zipOnly bool) {
260 // We only use the zip file anyway.