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