zoneinfo_plan9.mx raw
1 // Copyright 2011 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 // Parse Plan 9 timezone(2) files.
6
7 package time
8
9 import (
10 "syscall"
11 )
12
13 var platformZoneSources [][]byte // none on Plan 9
14
15 func isSpace(r rune) bool {
16 return r == ' ' || r == '\t' || r == '\n'
17 }
18
19 // Copied from strings to avoid a dependency.
20 func fields(s []byte) [][]byte {
21 // First count the fields.
22 n := 0
23 inField := false
24 for _, rune := range s {
25 wasInField := inField
26 inField = !isSpace(rune)
27 if inField && !wasInField {
28 n++
29 }
30 }
31
32 // Now create them.
33 a := [][]byte{:n}
34 na := 0
35 fieldStart := -1 // Set to -1 when looking for start of field.
36 for i, rune := range s {
37 if isSpace(rune) {
38 if fieldStart >= 0 {
39 a[na] = s[fieldStart:i]
40 na++
41 fieldStart = -1
42 }
43 } else if fieldStart == -1 {
44 fieldStart = i
45 }
46 }
47 if fieldStart >= 0 { // Last field might end at EOF.
48 a[na] = s[fieldStart:]
49 }
50 return a
51 }
52
53 func loadZoneDataPlan9(s []byte) (l *Location, err error) {
54 f := fields(s)
55 if len(f) < 4 {
56 if len(f) == 2 && f[0] == "GMT" {
57 return UTC, nil
58 }
59 return nil, errBadData
60 }
61
62 var zones [2]zone
63
64 // standard timezone offset
65 o, err := atoi(f[1])
66 if err != nil {
67 return nil, errBadData
68 }
69 zones[0] = zone{name: f[0], offset: o, isDST: false}
70
71 // alternate timezone offset
72 o, err = atoi(f[3])
73 if err != nil {
74 return nil, errBadData
75 }
76 zones[1] = zone{name: f[2], offset: o, isDST: true}
77
78 // transition time pairs
79 var tx []zoneTrans
80 f = f[4:]
81 for i := 0; i < len(f); i++ {
82 zi := 0
83 if i%2 == 0 {
84 zi = 1
85 }
86 t, err := atoi(f[i])
87 if err != nil {
88 return nil, errBadData
89 }
90 t -= zones[0].offset
91 tx = append(tx, zoneTrans{when: int64(t), index: uint8(zi)})
92 }
93
94 // Committed to succeed.
95 l = &Location{zone: zones[:], tx: tx}
96
97 // Fill in the cache with information about right now,
98 // since that will be the most common lookup.
99 sec, _, _ := runtimeNow()
100 for i := range tx {
101 if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) {
102 l.cacheStart = tx[i].when
103 l.cacheEnd = omega
104 if i+1 < len(tx) {
105 l.cacheEnd = tx[i+1].when
106 }
107 l.cacheZone = &l.zone[tx[i].index]
108 }
109 }
110
111 return l, nil
112 }
113
114 func loadZoneFilePlan9(name []byte) (*Location, error) {
115 b, err := readFile(name)
116 if err != nil {
117 return nil, err
118 }
119 return loadZoneDataPlan9([]byte(b))
120 }
121
122 func initLocal() {
123 t, ok := syscall.Getenv("timezone")
124 if ok {
125 if z, err := loadZoneDataPlan9(t); err == nil {
126 localLoc = *z
127 return
128 }
129 } else {
130 if z, err := loadZoneFilePlan9("/adm/timezone/local"); err == nil {
131 localLoc = *z
132 localLoc.name = "Local"
133 return
134 }
135 }
136
137 // Fall back to UTC.
138 localLoc.name = "UTC"
139 }
140