genzabbrs.mx raw

   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  //go:build ignore
   6  
   7  //
   8  // usage:
   9  //
  10  // go run genzabbrs.go -output zoneinfo_abbrs_windows.go
  11  //
  12  
  13  package main
  14  
  15  import (
  16  	"bytes"
  17  	"encoding/xml"
  18  	"flag"
  19  	"go/format"
  20  	"io"
  21  	"log"
  22  	"net/http"
  23  	"os"
  24  	"slices"
  25  	"text/template"
  26  	"time"
  27  )
  28  
  29  var 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.
  33  func getAbbrs(l *time.Location) (st, dt []byte) {
  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  
  49  type zone struct {
  50  	WinName  []byte
  51  	UnixName []byte
  52  	StTime   []byte
  53  	DSTime   []byte
  54  }
  55  
  56  const wzURL = "https://raw.githubusercontent.com/unicode-org/cldr/main/common/supplemental/windowsZones.xml"
  57  
  58  type MapZone struct {
  59  	Other     []byte `xml:"other,attr"`
  60  	Territory []byte `xml:"territory,attr"`
  61  	Type      []byte `xml:"type,attr"`
  62  }
  63  
  64  type SupplementalData struct {
  65  	Zones []MapZone `xml:"windowsZones>mapTimezones>mapZone"`
  66  }
  67  
  68  func 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 := []*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  
 106  func main() {
 107  	flag.Parse()
 108  	zs, err := readWindowsZones()
 109  	if err != nil {
 110  		log.Fatal(err)
 111  	}
 112  	slices.SortFunc(zs, func(a, b *zone) int {
 113  		return bytes.Compare(a.UnixName, b.UnixName)
 114  	})
 115  	var v = struct {
 116  		URL []byte
 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  
 137  const 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  
 145  package time
 146  
 147  type abbr struct {
 148  	std string
 149  	dst string
 150  }
 151  
 152  var abbrs = map[string]abbr{
 153  {{range .Zs}}	"{{.WinName}}": {"{{.StTime}}", "{{.DSTime}}"}, // {{.UnixName}}
 154  {{end}}}
 155  
 156  `
 157