diff options
author | Alex Brainman <alex.brainman@gmail.com> | 2013-07-10 15:34:24 +1000 |
---|---|---|
committer | Alex Brainman <alex.brainman@gmail.com> | 2013-07-10 15:34:24 +1000 |
commit | 411404df6e32d20ebb34ed624d43df190bd23f23 (patch) | |
tree | d39e11e4eac85cb21742a4a18a50e4c44d0b49f9 /src/pkg/time/zoneinfo_windows.go | |
parent | 39d3806a4ecf8f7547b285774a4c294183f5f441 (diff) | |
download | go-411404df6e32d20ebb34ed624d43df190bd23f23.tar.gz |
time: find correct zone abbreviations even on non-English windows systems
Fixes issue 5783
R=golang-dev, r
CC=golang-dev
https://codereview.appspot.com/10956043
Diffstat (limited to 'src/pkg/time/zoneinfo_windows.go')
-rw-r--r-- | src/pkg/time/zoneinfo_windows.go | 83 |
1 files changed, 82 insertions, 1 deletions
diff --git a/src/pkg/time/zoneinfo_windows.go b/src/pkg/time/zoneinfo_windows.go index 541327f05..1e18ad295 100644 --- a/src/pkg/time/zoneinfo_windows.go +++ b/src/pkg/time/zoneinfo_windows.go @@ -8,6 +8,7 @@ import ( "errors" "runtime" "syscall" + "unsafe" ) // TODO(rsc): Fall back to copy of zoneinfo files. @@ -17,6 +18,78 @@ import ( // The implementation assumes that this year's rules for daylight savings // time apply to all previous and future years as well. +// 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 @@ -33,8 +106,16 @@ func abbrev(z *syscall.Timezoneinformation) (std, dst string) { stdName := syscall.UTF16ToString(z.StandardName[:]) a, ok := abbrs[stdName] if !ok { - // fallback to using capital letters 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 |