mirror of
https://github.com/golang/go
synced 2024-11-26 03:57:57 -07:00
time: find correct zone abbreviations even on non-English windows systems
Fixes #5783 R=golang-dev, r CC=golang-dev https://golang.org/cl/10956043
This commit is contained in:
parent
8268eadb9e
commit
231dfd9049
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user