github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/time/zoneinfo_windows.go (about) 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. 4 5 package time 6 7 import ( 8 "errors" 9 "internal/syscall/windows/registry" 10 "runtime" 11 "syscall" 12 ) 13 14 //go:generate go run genzabbrs.go -output zoneinfo_abbrs_windows.go 15 16 // TODO(rsc): Fall back to copy of zoneinfo files. 17 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. 22 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 30 } 31 defer k.Close() 32 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") 39 } 40 } 41 if err != nil { // Fallback to Std and Dlt 42 if std, _, err = k.GetStringValue("Std"); err != nil { 43 return false, err 44 } 45 if dlt, _, err = k.GetStringValue("Dlt"); err != nil { 46 return false, err 47 } 48 } 49 50 if std != stdname { 51 return false, nil 52 } 53 if dlt != dstname && dstname != stdname { 54 return false, nil 55 } 56 return true, nil 57 } 58 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 65 } 66 defer k.Close() 67 68 names, err := k.ReadSubKeyNames(-1) 69 if err != nil { 70 return "", err 71 } 72 for _, name := range names { 73 matched, err := matchZoneKey(k, name, stdname, dstname) 74 if err == nil && matched { 75 return name, nil 76 } 77 } 78 return "", errors.New(`English name for time zone "` + stdname + `" not found in registry`) 79 } 80 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) 87 } 88 } 89 return string(short) 90 } 91 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 104 } 105 } 106 // fallback to using capital letters 107 return extractCAPS(stdName), extractCAPS(dstName) 108 } 109 return a.std, a.dst 110 } 111 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 126 } 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 135 } 136 } 137 return t.sec + int64(day-1)*secondsPerDay + internalToUnix 138 } 139 140 func initLocalFromTZI(i *syscall.Timezoneinformation) { 141 l := &localLoc 142 143 l.name = "Local" 144 145 nzone := 1 146 if i.StandardDate.Month > 0 { 147 nzone++ 148 } 149 l.zone = make([]zone, nzone) 150 151 stdname, dstname := abbrev(i) 152 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 165 } 166 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 171 172 dst := &l.zone[1] 173 dst.name = dstname 174 dst.offset = -int(i.Bias+i.DaylightBias) * 60 175 dst.isDST = true 176 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 186 } 187 188 // 2 tx per year, 100 years on each side of this year 189 l.tx = make([]zoneTrans, 400) 190 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++ 199 200 tx = &l.tx[txi] 201 tx.when = pseudoUnix(y, d1) - int64(l.zone[i0].offset) 202 tx.index = uint8(i1) 203 txi++ 204 } 205 } 206 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', 211 }, 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', 215 }, 216 DaylightDate: syscall.Systemtime{Month: 3, Day: 2, Hour: 2}, 217 DaylightBias: -60, 218 } 219 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', 224 }, 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', 228 }, 229 DaylightDate: syscall.Systemtime{Month: 10, Day: 1, Hour: 2}, 230 DaylightBias: -60, 231 } 232 233 func initTestingZone() { 234 initLocalFromTZI(&usPacific) 235 } 236 237 func initAusTestingZone() { 238 initLocalFromTZI(&aus) 239 } 240 241 func initLocal() { 242 var i syscall.Timezoneinformation 243 if _, err := syscall.GetTimeZoneInformation(&i); err != nil { 244 localLoc.name = "UTC" 245 return 246 } 247 initLocalFromTZI(&i) 248 } 249 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 254 } 255 z.name = name 256 return z, nil 257 } 258 259 func forceZipFileForTesting(zipOnly bool) { 260 // We only use the zip file anyway. 261 }