github.com/AndrienkoAleksandr/go@v0.0.19/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 "syscall" 11 ) 12 13 var platformZoneSources []string // none: Windows uses system calls instead 14 15 // TODO(rsc): Fall back to copy of zoneinfo files. 16 17 // BUG(brainman,rsc): On Windows, the operating system does not provide complete 18 // time zone information. 19 // The implementation assumes that this year's rules for daylight savings 20 // time apply to all previous and future years as well. 21 22 // matchZoneKey checks if stdname and dstname match the corresponding key 23 // values "MUI_Std" and MUI_Dlt" or "Std" and "Dlt" (the latter down-level 24 // from Vista) in the kname key stored under the open registry key zones. 25 func matchZoneKey(zones registry.Key, kname string, stdname, dstname string) (matched bool, err2 error) { 26 k, err := registry.OpenKey(zones, kname, registry.READ) 27 if err != nil { 28 return false, err 29 } 30 defer k.Close() 31 32 var std, dlt string 33 if err = registry.LoadRegLoadMUIString(); err == nil { 34 // Try MUI_Std and MUI_Dlt first, fallback to Std and Dlt if *any* error occurs 35 std, err = k.GetMUIStringValue("MUI_Std") 36 if err == nil { 37 dlt, err = k.GetMUIStringValue("MUI_Dlt") 38 } 39 } 40 if err != nil { // Fallback to Std and Dlt 41 if std, _, err = k.GetStringValue("Std"); err != nil { 42 return false, err 43 } 44 if dlt, _, err = k.GetStringValue("Dlt"); err != nil { 45 return false, err 46 } 47 } 48 49 if std != stdname { 50 return false, nil 51 } 52 if dlt != dstname && dstname != stdname { 53 return false, nil 54 } 55 return true, nil 56 } 57 58 // toEnglishName searches the registry for an English name of a time zone 59 // whose zone names are stdname and dstname and returns the English name. 60 func toEnglishName(stdname, dstname string) (string, error) { 61 k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones`, registry.ENUMERATE_SUB_KEYS|registry.QUERY_VALUE) 62 if err != nil { 63 return "", err 64 } 65 defer k.Close() 66 67 names, err := k.ReadSubKeyNames() 68 if err != nil { 69 return "", err 70 } 71 for _, name := range names { 72 matched, err := matchZoneKey(k, name, stdname, dstname) 73 if err == nil && matched { 74 return name, nil 75 } 76 } 77 return "", errors.New(`English name for time zone "` + stdname + `" not found in registry`) 78 } 79 80 // extractCAPS extracts capital letters from description desc. 81 func extractCAPS(desc string) string { 82 var short []rune 83 for _, c := range desc { 84 if 'A' <= c && c <= 'Z' { 85 short = append(short, c) 86 } 87 } 88 return string(short) 89 } 90 91 // abbrev returns the abbreviations to use for the given zone z. 92 func abbrev(z *syscall.Timezoneinformation) (std, dst string) { 93 stdName := syscall.UTF16ToString(z.StandardName[:]) 94 a, ok := abbrs[stdName] 95 if !ok { 96 dstName := syscall.UTF16ToString(z.DaylightName[:]) 97 // Perhaps stdName is not English. Try to convert it. 98 englishName, err := toEnglishName(stdName, dstName) 99 if err == nil { 100 a, ok = abbrs[englishName] 101 if ok { 102 return a.std, a.dst 103 } 104 } 105 // fallback to using capital letters 106 return extractCAPS(stdName), extractCAPS(dstName) 107 } 108 return a.std, a.dst 109 } 110 111 // pseudoUnix returns the pseudo-Unix time (seconds since Jan 1 1970 *LOCAL TIME*) 112 // denoted by the system date+time d in the given year. 113 // It is up to the caller to convert this local time into a UTC-based time. 114 func pseudoUnix(year int, d *syscall.Systemtime) int64 { 115 // Windows specifies daylight savings information in "day in month" format: 116 // d.Month is month number (1-12) 117 // d.DayOfWeek is appropriate weekday (Sunday=0 to Saturday=6) 118 // d.Day is week within the month (1 to 5, where 5 is last week of the month) 119 // d.Hour, d.Minute and d.Second are absolute time 120 day := 1 121 t := Date(year, Month(d.Month), day, int(d.Hour), int(d.Minute), int(d.Second), 0, UTC) 122 i := int(d.DayOfWeek) - int(t.Weekday()) 123 if i < 0 { 124 i += 7 125 } 126 day += i 127 if week := int(d.Day) - 1; week < 4 { 128 day += week * 7 129 } else { 130 // "Last" instance of the day. 131 day += 4 * 7 132 if day > daysIn(Month(d.Month), year) { 133 day -= 7 134 } 135 } 136 return t.sec() + int64(day-1)*secondsPerDay + internalToUnix 137 } 138 139 func initLocalFromTZI(i *syscall.Timezoneinformation) { 140 l := &localLoc 141 142 l.name = "Local" 143 144 nzone := 1 145 if i.StandardDate.Month > 0 { 146 nzone++ 147 } 148 l.zone = make([]zone, nzone) 149 150 stdname, dstname := abbrev(i) 151 152 std := &l.zone[0] 153 std.name = stdname 154 if nzone == 1 { 155 // No daylight savings. 156 std.offset = -int(i.Bias) * 60 157 l.cacheStart = alpha 158 l.cacheEnd = omega 159 l.cacheZone = std 160 l.tx = make([]zoneTrans, 1) 161 l.tx[0].when = l.cacheStart 162 l.tx[0].index = 0 163 return 164 } 165 166 // StandardBias must be ignored if StandardDate is not set, 167 // so this computation is delayed until after the nzone==1 168 // return above. 169 std.offset = -int(i.Bias+i.StandardBias) * 60 170 171 dst := &l.zone[1] 172 dst.name = dstname 173 dst.offset = -int(i.Bias+i.DaylightBias) * 60 174 dst.isDST = true 175 176 // Arrange so that d0 is first transition date, d1 second, 177 // i0 is index of zone after first transition, i1 second. 178 d0 := &i.StandardDate 179 d1 := &i.DaylightDate 180 i0 := 0 181 i1 := 1 182 if d0.Month > d1.Month { 183 d0, d1 = d1, d0 184 i0, i1 = i1, i0 185 } 186 187 // 2 tx per year, 100 years on each side of this year 188 l.tx = make([]zoneTrans, 400) 189 190 t := Now().UTC() 191 year := t.Year() 192 txi := 0 193 for y := year - 100; y < year+100; y++ { 194 tx := &l.tx[txi] 195 tx.when = pseudoUnix(y, d0) - int64(l.zone[i1].offset) 196 tx.index = uint8(i0) 197 txi++ 198 199 tx = &l.tx[txi] 200 tx.when = pseudoUnix(y, d1) - int64(l.zone[i0].offset) 201 tx.index = uint8(i1) 202 txi++ 203 } 204 } 205 206 var usPacific = syscall.Timezoneinformation{ 207 Bias: 8 * 60, 208 StandardName: [32]uint16{ 209 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e', 210 }, 211 StandardDate: syscall.Systemtime{Month: 11, Day: 1, Hour: 2}, 212 DaylightName: [32]uint16{ 213 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e', 214 }, 215 DaylightDate: syscall.Systemtime{Month: 3, Day: 2, Hour: 2}, 216 DaylightBias: -60, 217 } 218 219 var aus = syscall.Timezoneinformation{ 220 Bias: -10 * 60, 221 StandardName: [32]uint16{ 222 'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e', 223 }, 224 StandardDate: syscall.Systemtime{Month: 4, Day: 1, Hour: 3}, 225 DaylightName: [32]uint16{ 226 'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e', 227 }, 228 DaylightDate: syscall.Systemtime{Month: 10, Day: 1, Hour: 2}, 229 DaylightBias: -60, 230 } 231 232 func initLocal() { 233 var i syscall.Timezoneinformation 234 if _, err := syscall.GetTimeZoneInformation(&i); err != nil { 235 localLoc.name = "UTC" 236 return 237 } 238 initLocalFromTZI(&i) 239 }