| // Copyright 2009 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package time | |
| import ( | |
| "errors" | |
| "internal/syscall/windows/registry" | |
| "runtime" | |
| "syscall" | |
| ) | |
| var zoneSources = []string{ | |
| runtime.GOROOT() + "/lib/time/zoneinfo.zip", | |
| } | |
| // TODO(rsc): Fall back to copy of zoneinfo files. | |
| // BUG(brainman,rsc): On Windows, the operating system does not provide complete | |
| // time zone information. | |
| // The implementation assumes that this year's rules for daylight savings | |
| // time apply to all previous and future years as well. | |
| // matchZoneKey checks if stdname and dstname match the corresponding key | |
| // values "MUI_Std" and MUI_Dlt" or "Std" and "Dlt" (the latter down-level | |
| // from Vista) in the kname key stored under the open registry key zones. | |
| func matchZoneKey(zones registry.Key, kname string, stdname, dstname string) (matched bool, err2 error) { | |
| k, err := registry.OpenKey(zones, kname, registry.READ) | |
| if err != nil { | |
| return false, err | |
| } | |
| defer k.Close() | |
| var std, dlt string | |
| if err = registry.LoadRegLoadMUIString(); err == nil { | |
| // Try MUI_Std and MUI_Dlt first, fallback to Std and Dlt if *any* error occurs | |
| std, err = k.GetMUIStringValue("MUI_Std") | |
| if err == nil { | |
| dlt, err = k.GetMUIStringValue("MUI_Dlt") | |
| } | |
| } | |
| if err != nil { // Fallback to Std and Dlt | |
| if std, _, err = k.GetStringValue("Std"); err != nil { | |
| return false, err | |
| } | |
| if dlt, _, err = k.GetStringValue("Dlt"); err != nil { | |
| return false, err | |
| } | |
| } | |
| if std != stdname { | |
| return false, nil | |
| } | |
| if dlt != dstname && dstname != stdname { | |
| return false, nil | |
| } | |
| return true, nil | |
| } | |
| // toEnglishName searches the registry for an English name of a time zone | |
| // whose zone names are stdname and dstname and returns the English name. | |
| func toEnglishName(stdname, dstname string) (string, error) { | |
| k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones`, registry.ENUMERATE_SUB_KEYS|registry.QUERY_VALUE) | |
| if err != nil { | |
| return "", err | |
| } | |
| defer k.Close() | |
| names, err := k.ReadSubKeyNames(-1) | |
| if err != nil { | |
| return "", err | |
| } | |
| for _, name := range names { | |
| matched, err := matchZoneKey(k, name, stdname, dstname) | |
| if err == nil && matched { | |
| return name, nil | |
| } | |
| } | |
| return "", errors.New(`English name for time zone "` + stdname + `" not found in registry`) | |
| } | |
| // extractCAPS extracts capital letters from description desc. | |
| func extractCAPS(desc string) string { | |
| var short []rune | |
| for _, c := range desc { | |
| if 'A' <= c && c <= 'Z' { | |
| short = append(short, c) | |
| } | |
| } | |
| return string(short) | |
| } | |
| // abbrev returns the abbreviations to use for the given zone z. | |
| func abbrev(z *syscall.Timezoneinformation) (std, dst string) { | |
| stdName := syscall.UTF16ToString(z.StandardName[:]) | |
| a, ok := abbrs[stdName] | |
| if !ok { | |
| dstName := syscall.UTF16ToString(z.DaylightName[:]) | |
| // Perhaps stdName is not English. Try to convert it. | |
| englishName, err := toEnglishName(stdName, dstName) | |
| if err == nil { | |
| a, ok = abbrs[englishName] | |
| if ok { | |
| return a.std, a.dst | |
| } | |
| } | |
| // fallback to using capital letters | |
| return extractCAPS(stdName), extractCAPS(dstName) | |
| } | |
| return a.std, a.dst | |
| } | |
| // pseudoUnix returns the pseudo-Unix time (seconds since Jan 1 1970 *LOCAL TIME*) | |
| // denoted by the system date+time d in the given year. | |
| // It is up to the caller to convert this local time into a UTC-based time. | |
| func pseudoUnix(year int, d *syscall.Systemtime) int64 { | |
| // Windows specifies daylight savings information in "day in month" format: | |
| // d.Month is month number (1-12) | |
| // d.DayOfWeek is appropriate weekday (Sunday=0 to Saturday=6) | |
| // d.Day is week within the month (1 to 5, where 5 is last week of the month) | |
| // d.Hour, d.Minute and d.Second are absolute time | |
| day := 1 | |
| t := Date(year, Month(d.Month), day, int(d.Hour), int(d.Minute), int(d.Second), 0, UTC) | |
| i := int(d.DayOfWeek) - int(t.Weekday()) | |
| if i < 0 { | |
| i += 7 | |
| } | |
| day += i | |
| if week := int(d.Day) - 1; week < 4 { | |
| day += week * 7 | |
| } else { | |
| // "Last" instance of the day. | |
| day += 4 * 7 | |
| if day > daysIn(Month(d.Month), year) { | |
| day -= 7 | |
| } | |
| } | |
| return t.sec() + int64(day-1)*secondsPerDay + internalToUnix | |
| } | |
| func initLocalFromTZI(i *syscall.Timezoneinformation) { | |
| l := &localLoc | |
| l.name = "Local" | |
| nzone := 1 | |
| if i.StandardDate.Month > 0 { | |
| nzone++ | |
| } | |
| l.zone = make([]zone, nzone) | |
| stdname, dstname := abbrev(i) | |
| std := &l.zone[0] | |
| std.name = stdname | |
| if nzone == 1 { | |
| // No daylight savings. | |
| std.offset = -int(i.Bias) * 60 | |
| l.cacheStart = alpha | |
| l.cacheEnd = omega | |
| l.cacheZone = std | |
| l.tx = make([]zoneTrans, 1) | |
| l.tx[0].when = l.cacheStart | |
| l.tx[0].index = 0 | |
| return | |
| } | |
| // StandardBias must be ignored if StandardDate is not set, | |
| // so this computation is delayed until after the nzone==1 | |
| // return above. | |
| std.offset = -int(i.Bias+i.StandardBias) * 60 | |
| dst := &l.zone[1] | |
| dst.name = dstname | |
| dst.offset = -int(i.Bias+i.DaylightBias) * 60 | |
| dst.isDST = true | |
| // Arrange so that d0 is first transition date, d1 second, | |
| // i0 is index of zone after first transition, i1 second. | |
| d0 := &i.StandardDate | |
| d1 := &i.DaylightDate | |
| i0 := 0 | |
| i1 := 1 | |
| if d0.Month > d1.Month { | |
| d0, d1 = d1, d0 | |
| i0, i1 = i1, i0 | |
| } | |
| // 2 tx per year, 100 years on each side of this year | |
| l.tx = make([]zoneTrans, 400) | |
| t := Now().UTC() | |
| year := t.Year() | |
| txi := 0 | |
| for y := year - 100; y < year+100; y++ { | |
| tx := &l.tx[txi] | |
| tx.when = pseudoUnix(y, d0) - int64(l.zone[i1].offset) | |
| tx.index = uint8(i0) | |
| txi++ | |
| tx = &l.tx[txi] | |
| tx.when = pseudoUnix(y, d1) - int64(l.zone[i0].offset) | |
| tx.index = uint8(i1) | |
| txi++ | |
| } | |
| } | |
| var usPacific = syscall.Timezoneinformation{ | |
| Bias: 8 * 60, | |
| StandardName: [32]uint16{ | |
| 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e', | |
| }, | |
| StandardDate: syscall.Systemtime{Month: 11, Day: 1, Hour: 2}, | |
| DaylightName: [32]uint16{ | |
| 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e', | |
| }, | |
| DaylightDate: syscall.Systemtime{Month: 3, Day: 2, Hour: 2}, | |
| DaylightBias: -60, | |
| } | |
| var aus = syscall.Timezoneinformation{ | |
| Bias: -10 * 60, | |
| StandardName: [32]uint16{ | |
| 'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e', | |
| }, | |
| StandardDate: syscall.Systemtime{Month: 4, Day: 1, Hour: 3}, | |
| DaylightName: [32]uint16{ | |
| 'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e', | |
| }, | |
| DaylightDate: syscall.Systemtime{Month: 10, Day: 1, Hour: 2}, | |
| DaylightBias: -60, | |
| } | |
| func initLocal() { | |
| var i syscall.Timezoneinformation | |
| if _, err := syscall.GetTimeZoneInformation(&i); err != nil { | |
| localLoc.name = "UTC" | |
| return | |
| } | |
| initLocalFromTZI(&i) | |
| } |