1// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// +build ignore
6
7//
8// usage:
9//
10// go run genzabbrs.go -output zoneinfo_abbrs_windows.go
11//
12
13package main
14
15import (
16	"bytes"
17	"encoding/xml"
18	"flag"
19	"go/format"
20	"io"
21	"log"
22	"net/http"
23	"os"
24	"sort"
25	"text/template"
26	"time"
27)
28
29var filename = flag.String("output", "zoneinfo_abbrs_windows.go", "output file name")
30
31// getAbbrs finds timezone abbreviations (standard and daylight saving time)
32// for location l.
33func getAbbrs(l *time.Location) (st, dt string) {
34	t := time.Date(time.Now().Year(), 0, 1, 0, 0, 0, 0, l)
35	abbr1, off1 := t.Zone()
36	for i := 0; i < 12; i++ {
37		t = t.AddDate(0, 1, 0)
38		abbr2, off2 := t.Zone()
39		if abbr1 != abbr2 {
40			if off2-off1 < 0 { // southern hemisphere
41				abbr1, abbr2 = abbr2, abbr1
42			}
43			return abbr1, abbr2
44		}
45	}
46	return abbr1, abbr1
47}
48
49type zone struct {
50	WinName  string
51	UnixName string
52	StTime   string
53	DSTime   string
54}
55
56const wzURL = "https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml"
57
58type MapZone struct {
59	Other     string `xml:"other,attr"`
60	Territory string `xml:"territory,attr"`
61	Type      string `xml:"type,attr"`
62}
63
64type SupplementalData struct {
65	Zones []MapZone `xml:"windowsZones>mapTimezones>mapZone"`
66}
67
68func readWindowsZones() ([]*zone, error) {
69	r, err := http.Get(wzURL)
70	if err != nil {
71		return nil, err
72	}
73	defer r.Body.Close()
74
75	data, err := io.ReadAll(r.Body)
76	if err != nil {
77		return nil, err
78	}
79
80	var sd SupplementalData
81	err = xml.Unmarshal(data, &sd)
82	if err != nil {
83		return nil, err
84	}
85	zs := make([]*zone, 0)
86	for _, z := range sd.Zones {
87		if z.Territory != "001" {
88			// to avoid dups. I don't know why.
89			continue
90		}
91		l, err := time.LoadLocation(z.Type)
92		if err != nil {
93			return nil, err
94		}
95		st, dt := getAbbrs(l)
96		zs = append(zs, &zone{
97			WinName:  z.Other,
98			UnixName: z.Type,
99			StTime:   st,
100			DSTime:   dt,
101		})
102	}
103	return zs, nil
104}
105
106func main() {
107	flag.Parse()
108	zs, err := readWindowsZones()
109	if err != nil {
110		log.Fatal(err)
111	}
112	sort.Slice(zs, func(i, j int) bool {
113		return zs[i].UnixName < zs[j].UnixName
114	})
115	var v = struct {
116		URL string
117		Zs  []*zone
118	}{
119		wzURL,
120		zs,
121	}
122	var buf bytes.Buffer
123	err = template.Must(template.New("prog").Parse(prog)).Execute(&buf, v)
124	if err != nil {
125		log.Fatal(err)
126	}
127	data, err := format.Source(buf.Bytes())
128	if err != nil {
129		log.Fatal(err)
130	}
131	err = os.WriteFile(*filename, data, 0644)
132	if err != nil {
133		log.Fatal(err)
134	}
135}
136
137const prog = `
138// Copyright 2013 The Go Authors. All rights reserved.
139// Use of this source code is governed by a BSD-style
140// license that can be found in the LICENSE file.
141
142// Code generated by genzabbrs.go; DO NOT EDIT.
143// Based on information from {{.URL}}
144
145package time
146
147type abbr struct {
148	std string
149	dst string
150}
151
152var abbrs = map[string]abbr{
153{{range .Zs}}	"{{.WinName}}": {"{{.StTime}}", "{{.DSTime}}"}, // {{.UnixName}}
154{{end}}}
155
156`
157