]> Cypherpunks repositories - gostls13.git/commitdiff
time: find correct zone abbreviations even on non-English windows systems
authorAlex Brainman <alex.brainman@gmail.com>
Wed, 10 Jul 2013 05:34:24 +0000 (15:34 +1000)
committerAlex Brainman <alex.brainman@gmail.com>
Wed, 10 Jul 2013 05:34:24 +0000 (15:34 +1000)
Fixes #5783

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/10956043

src/pkg/time/zoneinfo_windows.go

index 541327f056c3cb04a834444c03e12f99dadfe8d8..1e18ad295dfe6f3f609cad64631d806164911d2d 100644 (file)
@@ -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