mirror of https://github.com/golang/go.git
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
This commit is contained in:
parent
9614119007
commit
07ea243d50
|
|
@ -0,0 +1,9 @@
|
|||
# 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
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// 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)
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
// 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}}}
|
||||
|
||||
`
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
// 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 {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
// 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)
|
||||
}
|
||||
Loading…
Reference in New Issue