github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/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 var zoneSources = []string{ 15 runtime.GOROOT() + "/lib/time/zoneinfo.zip", 16 } 17 18 // TODO(rsc): Fall back to copy of zoneinfo files. 19 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. 24 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) 30 if err != nil { 31 return false, err 32 } 33 defer k.Close() 34 35 var std, dlt string 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") 39 if err == nil { 40 dlt, err = k.GetMUIStringValue("MUI_Dlt") 41 } 42 } 43 if err != nil { // Fallback to Std and Dlt 44 if std, _, err = k.GetStringValue("Std"); err != nil { 45 return false, err 46 } 47 if dlt, _, err = k.GetStringValue("Dlt"); err != nil { 48 return false, err 49 } 50 } 51 52 if std != stdname { 53 return false, nil 54 } 55 if dlt != dstname && dstname != stdname { 56 return false, nil 57 } 58 return true, nil 59 } 60 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) 65 if err != nil { 66 return "", err 67 } 68 defer k.Close() 69 70 names, err := k.ReadSubKeyNames(-1) 71 if err != nil { 72 return "", err 73 } 74 for _, name := range names { 75 matched, err := matchZoneKey(k, name, stdname, dstname) 76 if err == nil && matched { 77 return name, nil 78 } 79 } 80 return "", errors.New(`English name for time zone "` + stdname + `" not found in registry`) 81 } 82 83 // extractCAPS extracts capital letters from description desc. 84 func extractCAPS(desc string) string { 85 var short []rune 86 for _, c := range desc { 87 if 'A' <= c && c <= 'Z' { 88 short = append(short, c) 89 } 90 } 91 return string(short) 92 } 93 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] 98 if !ok { 99 dstName := syscall.UTF16ToString(z.DaylightName[:]) 100 // Perhaps stdName is not English. Try to convert it. 101 englishName, err := toEnglishName(stdName, dstName) 102 if err == nil { 103 a, ok = abbrs[englishName] 104 if ok { 105 return a.std, a.dst 106 } 107 } 108 // fallback to using capital letters 109 return extractCAPS(stdName), extractCAPS(dstName) 110 } 111 return a.std, a.dst 112 } 113 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 123 day := 1 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()) 126 if i < 0 { 127 i += 7 128 } 129 day += i 130 if week := int(d.Day) - 1; week < 4 { 131 day += week * 7 132 } else { 133 // "Last" instance of the day. 134 day += 4 * 7 135 if day > daysIn(Month(d.Month), year) { 136 day -= 7 137 } 138 } 139 return t.sec() + int64(day-1)*secondsPerDay + internalToUnix 140 } 141 142 func initLocalFromTZI(i *syscall.Timezoneinformation) { 143 l := &localLoc 144 145 l.name = "Local" 146 147 nzone := 1 148 if i.StandardDate.Month > 0 { 149 nzone++ 150 } 151 l.zone = make([]zone, nzone) 152 153 stdname, dstname := abbrev(i) 154 155 std := &l.zone[0] 156 std.name = stdname 157 if nzone == 1 { 158 // No daylight savings. 159 std.offset = -int(i.Bias) * 60 160 l.cacheStart = alpha 161 l.cacheEnd = omega 162 l.cacheZone = std 163 l.tx = make([]zoneTrans, 1) 164 l.tx[0].when = l.cacheStart 165 l.tx[0].index = 0 166 return 167 } 168 169 // StandardBias must be ignored if StandardDate is not set, 170 // so this computation is delayed until after the nzone==1 171 // return above. 172 std.offset = -int(i.Bias+i.StandardBias) * 60 173 174 dst := &l.zone[1] 175 dst.name = dstname 176 dst.offset = -int(i.Bias+i.DaylightBias) * 60 177 dst.isDST = true 178 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 183 i0 := 0 184 i1 := 1 185 if d0.Month > d1.Month { 186 d0, d1 = d1, d0 187 i0, i1 = i1, i0 188 } 189 190 // 2 tx per year, 100 years on each side of this year 191 l.tx = make([]zoneTrans, 400) 192 193 t := Now().UTC() 194 year := t.Year() 195 txi := 0 196 for y := year - 100; y < year+100; y++ { 197 tx := &l.tx[txi] 198 tx.when = pseudoUnix(y, d0) - int64(l.zone[i1].offset) 199 tx.index = uint8(i0) 200 txi++ 201 202 tx = &l.tx[txi] 203 tx.when = pseudoUnix(y, d1) - int64(l.zone[i0].offset) 204 tx.index = uint8(i1) 205 txi++ 206 } 207 } 208 209 var usPacific = syscall.Timezoneinformation{ 210 Bias: 8 * 60, 211 StandardName: [32]uint16{ 212 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e', 213 }, 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', 217 }, 218 DaylightDate: syscall.Systemtime{Month: 3, Day: 2, Hour: 2}, 219 DaylightBias: -60, 220 } 221 222 var aus = syscall.Timezoneinformation{ 223 Bias: -10 * 60, 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', 226 }, 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', 230 }, 231 DaylightDate: syscall.Systemtime{Month: 10, Day: 1, Hour: 2}, 232 DaylightBias: -60, 233 } 234 235 func initLocal() { 236 var i syscall.Timezoneinformation 237 if _, err := syscall.GetTimeZoneInformation(&i); err != nil { 238 localLoc.name = "UTC" 239 return 240 } 241 initLocalFromTZI(&i) 242 }