Commit 07ea243d authored by Alex Brainman's avatar Alex Brainman

time: provide timezone abbreviations on windows

Use http://unicode.org/cldr/data/common/supplemental/windowsZones.xml
to generate the map.

Fixes #4838.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/9997043
parent 96141190
# Copyright 2013 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.
genzabbrs: genzabbrs.go
go build genzabbrs.go
windows: genzabbrs
./genzabbrs | gofmt >zoneinfo_abbrs_windows.go
// Copyright 2013 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
func ForceAusForTesting() {
ResetLocalOnceForTest()
localOnce.Do(initAusTestingZone)
}
// Copyright 2013 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.
// +build ignore
//
// usage:
//
// go run genzabbrs.go | gofmt > $GOROOT/src/pkg/time/zoneinfo_abbrs_windows.go
//
package main
import (
"encoding/xml"
"io/ioutil"
"log"
"net/http"
"os"
"sort"
"text/template"
"time"
)
// getAbbrs finds timezone abbreviations (standard and daylight saving time)
// for location l.
func getAbbrs(l *time.Location) (st, dt string) {
t := time.Date(time.Now().Year(), 0, 0, 0, 0, 0, 0, l)
abbr1, off1 := t.Zone()
for i := 0; i < 12; i++ {
t = t.AddDate(0, 1, 0)
abbr2, off2 := t.Zone()
if abbr1 != abbr2 {
if off2-off1 < 0 { // southern hemisphere
abbr1, abbr2 = abbr2, abbr1
}
return abbr1, abbr2
}
}
return abbr1, abbr1
}
type zone struct {
WinName string
UnixName string
StTime string
DSTime string
}
type zones []*zone
func (zs zones) Len() int { return len(zs) }
func (zs zones) Swap(i, j int) { zs[i], zs[j] = zs[j], zs[i] }
func (zs zones) Less(i, j int) bool { return zs[i].UnixName < zs[j].UnixName }
const wzURL = "http://unicode.org/cldr/data/common/supplemental/windowsZones.xml"
type MapZone struct {
Other string `xml:"other,attr"`
Territory string `xml:"territory,attr"`
Type string `xml:"type,attr"`
}
type SupplementalData struct {
Zones []MapZone `xml:"windowsZones>mapTimezones>mapZone"`
}
func readWindowsZones() (zones, error) {
r, err := http.Get(wzURL)
if err != nil {
return nil, err
}
defer r.Body.Close()
data, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, err
}
var sd SupplementalData
err = xml.Unmarshal(data, &sd)
if err != nil {
return nil, err
}
zs := make(zones, 0)
for _, z := range sd.Zones {
if z.Territory != "001" {
// to avoid dups. I don't know why.
continue
}
l, err := time.LoadLocation(z.Type)
if err != nil {
return nil, err
}
st, dt := getAbbrs(l)
zs = append(zs, &zone{
WinName: z.Other,
UnixName: z.Type,
StTime: st,
DSTime: dt,
})
}
return zs, nil
}
func main() {
zs, err := readWindowsZones()
if err != nil {
log.Fatal(err)
}
sort.Sort(zs)
var v = struct {
URL string
Zs zones
}{
wzURL,
zs,
}
err = template.Must(template.New("prog").Parse(prog)).Execute(os.Stdout, v)
if err != nil {
log.Fatal(err)
}
}
const prog = `
// Copyright 2013 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.
// generated by genzabbrs.go from
// {{.URL}}
package time
type abbr struct {
std string
dst string
}
var abbrs = map[string]abbr{
{{range .Zs}} "{{.WinName}}": {"{{.StTime}}", "{{.DSTime}}"}, // {{.UnixName}}
{{end}}}
`
// Copyright 2013 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.
// generated by genzabbrs.go from
// http://unicode.org/cldr/data/common/supplemental/windowsZones.xml
package time
type abbr struct {
std string
dst string
}
var abbrs = map[string]abbr{
"Egypt Standard Time": {"EET", "EET"}, // Africa/Cairo
"Morocco Standard Time": {"WET", "WEST"}, // Africa/Casablanca
"South Africa Standard Time": {"SAST", "SAST"}, // Africa/Johannesburg
"W. Central Africa Standard Time": {"WAT", "WAT"}, // Africa/Lagos
"E. Africa Standard Time": {"EAT", "EAT"}, // Africa/Nairobi
"Namibia Standard Time": {"WAT", "WAST"}, // Africa/Windhoek
"Alaskan Standard Time": {"AKST", "AKDT"}, // America/Anchorage
"Paraguay Standard Time": {"PYT", "PYST"}, // America/Asuncion
"Bahia Standard Time": {"BRT", "BRST"}, // America/Bahia
"SA Pacific Standard Time": {"COT", "COT"}, // America/Bogota
"Argentina Standard Time": {"ART", "ART"}, // America/Buenos_Aires
"Venezuela Standard Time": {"VET", "VET"}, // America/Caracas
"SA Eastern Standard Time": {"GFT", "GFT"}, // America/Cayenne
"Central Standard Time": {"CST", "CDT"}, // America/Chicago
"Mountain Standard Time (Mexico)": {"MST", "MDT"}, // America/Chihuahua
"Central Brazilian Standard Time": {"AMT", "AMST"}, // America/Cuiaba
"Mountain Standard Time": {"MST", "MDT"}, // America/Denver
"Greenland Standard Time": {"WGT", "WGST"}, // America/Godthab
"Central America Standard Time": {"CST", "CST"}, // America/Guatemala
"Atlantic Standard Time": {"AST", "ADT"}, // America/Halifax
"US Eastern Standard Time": {"EST", "EDT"}, // America/Indianapolis
"SA Western Standard Time": {"BOT", "BOT"}, // America/La_Paz
"Pacific Standard Time": {"PST", "PDT"}, // America/Los_Angeles
"Central Standard Time (Mexico)": {"CST", "CDT"}, // America/Mexico_City
"Montevideo Standard Time": {"UYT", "UYST"}, // America/Montevideo
"Eastern Standard Time": {"EST", "EDT"}, // America/New_York
"US Mountain Standard Time": {"MST", "MST"}, // America/Phoenix
"Canada Central Standard Time": {"CST", "CST"}, // America/Regina
"Pacific Standard Time (Mexico)": {"PST", "PDT"}, // America/Santa_Isabel
"Pacific SA Standard Time": {"CLT", "CLST"}, // America/Santiago
"E. South America Standard Time": {"BRT", "BRST"}, // America/Sao_Paulo
"Newfoundland Standard Time": {"NST", "NDT"}, // America/St_Johns
"Central Asia Standard Time": {"ALMT", "ALMT"}, // Asia/Almaty
"Jordan Standard Time": {"EET", "EEST"}, // Asia/Amman
"Arabic Standard Time": {"AST", "AST"}, // Asia/Baghdad
"Azerbaijan Standard Time": {"AZT", "AZST"}, // Asia/Baku
"SE Asia Standard Time": {"ICT", "ICT"}, // Asia/Bangkok
"Middle East Standard Time": {"EET", "EEST"}, // Asia/Beirut
"India Standard Time": {"IST", "IST"}, // Asia/Calcutta
"Sri Lanka Standard Time": {"IST", "IST"}, // Asia/Colombo
"Syria Standard Time": {"EET", "EEST"}, // Asia/Damascus
"Bangladesh Standard Time": {"BDT", "BDT"}, // Asia/Dhaka
"Arabian Standard Time": {"GST", "GST"}, // Asia/Dubai
"North Asia East Standard Time": {"IRKT", "IRKT"}, // Asia/Irkutsk
"Israel Standard Time": {"IST", "IDT"}, // Asia/Jerusalem
"Afghanistan Standard Time": {"AFT", "AFT"}, // Asia/Kabul
"Pakistan Standard Time": {"PKT", "PKT"}, // Asia/Karachi
"Nepal Standard Time": {"NPT", "NPT"}, // Asia/Katmandu
"North Asia Standard Time": {"KRAT", "KRAT"}, // Asia/Krasnoyarsk
"Magadan Standard Time": {"MAGT", "MAGT"}, // Asia/Magadan
"E. Europe Standard Time": {"EET", "EEST"}, // Asia/Nicosia
"N. Central Asia Standard Time": {"NOVT", "NOVT"}, // Asia/Novosibirsk
"Myanmar Standard Time": {"MMT", "MMT"}, // Asia/Rangoon
"Arab Standard Time": {"AST", "AST"}, // Asia/Riyadh
"Korea Standard Time": {"KST", "KST"}, // Asia/Seoul
"China Standard Time": {"CST", "CST"}, // Asia/Shanghai
"Singapore Standard Time": {"SGT", "SGT"}, // Asia/Singapore
"Taipei Standard Time": {"CST", "CST"}, // Asia/Taipei
"West Asia Standard Time": {"UZT", "UZT"}, // Asia/Tashkent
"Georgian Standard Time": {"GET", "GET"}, // Asia/Tbilisi
"Iran Standard Time": {"IRST", "IRDT"}, // Asia/Tehran
"Tokyo Standard Time": {"JST", "JST"}, // Asia/Tokyo
"Ulaanbaatar Standard Time": {"ULAT", "ULAT"}, // Asia/Ulaanbaatar
"Vladivostok Standard Time": {"VLAT", "VLAT"}, // Asia/Vladivostok
"Yakutsk Standard Time": {"YAKT", "YAKT"}, // Asia/Yakutsk
"Ekaterinburg Standard Time": {"YEKT", "YEKT"}, // Asia/Yekaterinburg
"Caucasus Standard Time": {"AMT", "AMT"}, // Asia/Yerevan
"Azores Standard Time": {"AZOT", "AZOST"}, // Atlantic/Azores
"Cape Verde Standard Time": {"CVT", "CVT"}, // Atlantic/Cape_Verde
"Greenwich Standard Time": {"GMT", "GMT"}, // Atlantic/Reykjavik
"Cen. Australia Standard Time": {"CST", "CST"}, // Australia/Adelaide
"E. Australia Standard Time": {"EST", "EST"}, // Australia/Brisbane
"AUS Central Standard Time": {"CST", "CST"}, // Australia/Darwin
"Tasmania Standard Time": {"EST", "EST"}, // Australia/Hobart
"W. Australia Standard Time": {"WST", "WST"}, // Australia/Perth
"AUS Eastern Standard Time": {"EST", "EST"}, // Australia/Sydney
"UTC": {"GMT", "GMT"}, // Etc/GMT
"UTC-11": {"GMT+11", "GMT+11"}, // Etc/GMT+11
"Dateline Standard Time": {"GMT+12", "GMT+12"}, // Etc/GMT+12
"UTC-02": {"GMT+2", "GMT+2"}, // Etc/GMT+2
"UTC+12": {"GMT-12", "GMT-12"}, // Etc/GMT-12
"W. Europe Standard Time": {"CET", "CEST"}, // Europe/Berlin
"GTB Standard Time": {"EET", "EEST"}, // Europe/Bucharest
"Central Europe Standard Time": {"CET", "CEST"}, // Europe/Budapest
"Turkey Standard Time": {"EET", "EEST"}, // Europe/Istanbul
"Kaliningrad Standard Time": {"FET", "FET"}, // Europe/Kaliningrad
"FLE Standard Time": {"EET", "EEST"}, // Europe/Kiev
"GMT Standard Time": {"GMT", "BST"}, // Europe/London
"Russian Standard Time": {"MSK", "MSK"}, // Europe/Moscow
"Romance Standard Time": {"CET", "CEST"}, // Europe/Paris
"Central European Standard Time": {"CET", "CEST"}, // Europe/Warsaw
"Mauritius Standard Time": {"MUT", "MUT"}, // Indian/Mauritius
"Samoa Standard Time": {"WST", "WST"}, // Pacific/Apia
"New Zealand Standard Time": {"NZST", "NZDT"}, // Pacific/Auckland
"Fiji Standard Time": {"FJT", "FJT"}, // Pacific/Fiji
"Central Pacific Standard Time": {"SBT", "SBT"}, // Pacific/Guadalcanal
"Hawaiian Standard Time": {"HST", "HST"}, // Pacific/Honolulu
"West Pacific Standard Time": {"PGT", "PGT"}, // Pacific/Port_Moresby
"Tonga Standard Time": {"TOT", "TOT"}, // Pacific/Tongatapu
}
......@@ -16,21 +16,11 @@ 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
// 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 +28,18 @@ 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 {
// fallback to using capital letters
dstName := syscall.UTF16ToString(z.DaylightName[:])
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 +77,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 +99,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 +146,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 {
......
// Copyright 2013 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_test
import (
"testing"
. "time"
)
func testZoneAbbr(t *testing.T) {
t1 := Now()
// discard nsec
t1 = Date(t1.Year(), t1.Month(), t1.Day(), t1.Hour(), t1.Minute(), t1.Second(), 0, t1.Location())
t2, err := Parse(RFC1123, t1.Format(RFC1123))
if err != nil {
t.Fatalf("Parse failed: %v", err)
}
if t1 != t2 {
t.Fatalf("t1 (%v) is not equal to t2 (%v)", t1, t2)
}
}
func TestLocalZoneAbbr(t *testing.T) {
ResetLocalOnceForTest() // reset the Once to trigger the race
defer ForceUSPacificForTesting()
testZoneAbbr(t)
}
func TestAusZoneAbbr(t *testing.T) {
ForceAusForTesting()
defer ForceUSPacificForTesting()
testZoneAbbr(t)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment