diff options
Diffstat (limited to 'libgo/go/time/zoneinfo_windows.go')
-rw-r--r-- | libgo/go/time/zoneinfo_windows.go | 134 |
1 files changed, 118 insertions, 16 deletions
diff --git a/libgo/go/time/zoneinfo_windows.go b/libgo/go/time/zoneinfo_windows.go index a8d3dcbfeb1..1e18ad295df 100644 --- a/libgo/go/time/zoneinfo_windows.go +++ b/libgo/go/time/zoneinfo_windows.go @@ -8,6 +8,7 @@ import ( "errors" "runtime" "syscall" + "unsafe" ) // TODO(rsc): Fall back to copy of zoneinfo files. @@ -16,21 +17,83 @@ import ( // time zone information. // The implementation assumes that this year's rules for daylight savings // time apply to all previous and future years as well. -// Also, time zone abbreviations are unavailable. The implementation constructs -// them using the capital letters from a longer time zone description. - -// abbrev returns the abbreviation to use for the given zone name. -func abbrev(name []uint16) string { - // name is 'Pacific Standard Time' but we want 'PST'. - // Extract just capital letters. It's not perfect but the - // information we need is not available from the kernel. - // Because time zone abbreviations are not unique, - // Windows refuses to expose them. - // - // http://social.msdn.microsoft.com/Forums/eu/vclanguage/thread/a87e1d25-fb71-4fe0-ae9c-a9578c9753eb - // http://stackoverflow.com/questions/4195948/windows-time-zone-abbreviations-in-asp-net + +// getKeyValue retrieves the string value kname associated with the open registry key kh. +func getKeyValue(kh syscall.Handle, kname string) (string, error) { + var buf [50]uint16 // buf needs to be large enough to fit zone descriptions + var typ uint32 + n := uint32(len(buf) * 2) // RegQueryValueEx's signature expects array of bytes, not uint16 + p, _ := syscall.UTF16PtrFromString(kname) + if err := syscall.RegQueryValueEx(kh, p, nil, &typ, (*byte)(unsafe.Pointer(&buf[0])), &n); err != nil { + return "", err + } + if typ != syscall.REG_SZ { // null terminated strings only + return "", errors.New("Key is not string") + } + return syscall.UTF16ToString(buf[:]), nil +} + +// matchZoneKey checks if stdname and dstname match the corresponding "Std" +// and "Dlt" key values in the kname key stored under the open registry key zones. +func matchZoneKey(zones syscall.Handle, kname string, stdname, dstname string) (matched bool, err2 error) { + var h syscall.Handle + p, _ := syscall.UTF16PtrFromString(kname) + if err := syscall.RegOpenKeyEx(zones, p, 0, syscall.KEY_READ, &h); err != nil { + return false, err + } + defer syscall.RegCloseKey(h) + + s, err := getKeyValue(h, "Std") + if err != nil { + return false, err + } + if s != stdname { + return false, nil + } + s, err = getKeyValue(h, "Dlt") + if err != nil { + return false, err + } + if s != dstname { + 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) { + var zones syscall.Handle + p, _ := syscall.UTF16PtrFromString(`SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones`) + if err := syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, p, 0, syscall.KEY_READ, &zones); err != nil { + return "", err + } + defer syscall.RegCloseKey(zones) + + var count uint32 + if err := syscall.RegQueryInfoKey(zones, nil, nil, nil, &count, nil, nil, nil, nil, nil, nil, nil); err != nil { + return "", err + } + + var buf [50]uint16 // buf needs to be large enough to fit zone descriptions + for i := uint32(0); i < count; i++ { + n := uint32(len(buf)) + if syscall.RegEnumKeyEx(zones, i, &buf[0], &n, nil, nil, nil, nil) != nil { + continue + } + kname := syscall.UTF16ToString(buf[:]) + matched, err := matchZoneKey(zones, kname, stdname, dstname) + if err == nil && matched { + return kname, nil + } + } + return "", errors.New(`English name for time zone "` + stdname + `" not found in registry`) +} + +// extractCAPS exracts capital letters from description desc. +func extractCAPS(desc string) string { var short []rune - for _, c := range name { + for _, c := range desc { if 'A' <= c && c <= 'Z' { short = append(short, rune(c)) } @@ -38,6 +101,26 @@ func abbrev(name []uint16) string { 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. @@ -75,8 +158,10 @@ func initLocalFromTZI(i *syscall.Timezoneinformation) { } l.zone = make([]zone, nzone) + stdname, dstname := abbrev(i) + std := &l.zone[0] - std.name = abbrev(i.StandardName[0:]) + std.name = stdname if nzone == 1 { // No daylight savings. std.offset = -int(i.Bias) * 60 @@ -95,7 +180,7 @@ func initLocalFromTZI(i *syscall.Timezoneinformation) { std.offset = -int(i.Bias+i.StandardBias) * 60 dst := &l.zone[1] - dst.name = abbrev(i.DaylightName[0:]) + dst.name = dstname dst.offset = -int(i.Bias+i.DaylightBias) * 60 dst.isDST = true @@ -142,10 +227,27 @@ var usPacific = syscall.Timezoneinformation{ 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 initTestingZone() { initLocalFromTZI(&usPacific) } +func initAusTestingZone() { + initLocalFromTZI(&aus) +} + func initLocal() { var i syscall.Timezoneinformation if _, err := syscall.GetTimeZoneInformation(&i); err != nil { |