OSDN Git Service

6685da7477b05f0d99754b5780e3083c3960cd68
[pf3gnuchains/gcc-fork.git] / libgo / go / time / zoneinfo_unix.go
1 // Copyright 2009 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 "zoneinfo" time zone file.
6 // This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others.
7 // See tzfile(5), http://en.wikipedia.org/wiki/Zoneinfo,
8 // and ftp://munnari.oz.au/pub/oldtz/
9
10 package time
11
12 import (
13         "io/ioutil"
14         "os"
15         "sync"
16 )
17
18 const (
19         headerSize = 4 + 16 + 4*7
20         zoneDir    = "/usr/share/zoneinfo/"
21         zoneDir2   = "/usr/share/lib/zoneinfo/"
22 )
23
24 // Simple I/O interface to binary blob of data.
25 type data struct {
26         p     []byte
27         error bool
28 }
29
30
31 func (d *data) read(n int) []byte {
32         if len(d.p) < n {
33                 d.p = nil
34                 d.error = true
35                 return nil
36         }
37         p := d.p[0:n]
38         d.p = d.p[n:]
39         return p
40 }
41
42 func (d *data) big4() (n uint32, ok bool) {
43         p := d.read(4)
44         if len(p) < 4 {
45                 d.error = true
46                 return 0, false
47         }
48         return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]), true
49 }
50
51 func (d *data) byte() (n byte, ok bool) {
52         p := d.read(1)
53         if len(p) < 1 {
54                 d.error = true
55                 return 0, false
56         }
57         return p[0], true
58 }
59
60
61 // Make a string by stopping at the first NUL
62 func byteString(p []byte) string {
63         for i := 0; i < len(p); i++ {
64                 if p[i] == 0 {
65                         return string(p[0:i])
66                 }
67         }
68         return string(p)
69 }
70
71 // Parsed representation
72 type zone struct {
73         utcoff int
74         isdst  bool
75         name   string
76 }
77
78 type zonetime struct {
79         time         int32 // transition time, in seconds since 1970 GMT
80         zone         *zone // the zone that goes into effect at that time
81         isstd, isutc bool  // ignored - no idea what these mean
82 }
83
84 func parseinfo(bytes []byte) (zt []zonetime, ok bool) {
85         d := data{bytes, false}
86
87         // 4-byte magic "TZif"
88         if magic := d.read(4); string(magic) != "TZif" {
89                 return nil, false
90         }
91
92         // 1-byte version, then 15 bytes of padding
93         var p []byte
94         if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' {
95                 return nil, false
96         }
97
98         // six big-endian 32-bit integers:
99         //      number of UTC/local indicators
100         //      number of standard/wall indicators
101         //      number of leap seconds
102         //      number of transition times
103         //      number of local time zones
104         //      number of characters of time zone abbrev strings
105         const (
106                 NUTCLocal = iota
107                 NStdWall
108                 NLeap
109                 NTime
110                 NZone
111                 NChar
112         )
113         var n [6]int
114         for i := 0; i < 6; i++ {
115                 nn, ok := d.big4()
116                 if !ok {
117                         return nil, false
118                 }
119                 n[i] = int(nn)
120         }
121
122         // Transition times.
123         txtimes := data{d.read(n[NTime] * 4), false}
124
125         // Time zone indices for transition times.
126         txzones := d.read(n[NTime])
127
128         // Zone info structures
129         zonedata := data{d.read(n[NZone] * 6), false}
130
131         // Time zone abbreviations.
132         abbrev := d.read(n[NChar])
133
134         // Leap-second time pairs
135         d.read(n[NLeap] * 8)
136
137         // Whether tx times associated with local time types
138         // are specified as standard time or wall time.
139         isstd := d.read(n[NStdWall])
140
141         // Whether tx times associated with local time types
142         // are specified as UTC or local time.
143         isutc := d.read(n[NUTCLocal])
144
145         if d.error { // ran out of data
146                 return nil, false
147         }
148
149         // If version == 2, the entire file repeats, this time using
150         // 8-byte ints for txtimes and leap seconds.
151         // We won't need those until 2106.
152
153         // Now we can build up a useful data structure.
154         // First the zone information.
155         //      utcoff[4] isdst[1] nameindex[1]
156         z := make([]zone, n[NZone])
157         for i := 0; i < len(z); i++ {
158                 var ok bool
159                 var n uint32
160                 if n, ok = zonedata.big4(); !ok {
161                         return nil, false
162                 }
163                 z[i].utcoff = int(n)
164                 var b byte
165                 if b, ok = zonedata.byte(); !ok {
166                         return nil, false
167                 }
168                 z[i].isdst = b != 0
169                 if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
170                         return nil, false
171                 }
172                 z[i].name = byteString(abbrev[b:])
173         }
174
175         // Now the transition time info.
176         zt = make([]zonetime, n[NTime])
177         for i := 0; i < len(zt); i++ {
178                 var ok bool
179                 var n uint32
180                 if n, ok = txtimes.big4(); !ok {
181                         return nil, false
182                 }
183                 zt[i].time = int32(n)
184                 if int(txzones[i]) >= len(z) {
185                         return nil, false
186                 }
187                 zt[i].zone = &z[txzones[i]]
188                 if i < len(isstd) {
189                         zt[i].isstd = isstd[i] != 0
190                 }
191                 if i < len(isutc) {
192                         zt[i].isutc = isutc[i] != 0
193                 }
194         }
195         return zt, true
196 }
197
198 func readinfofile(name string) ([]zonetime, bool) {
199         buf, err := ioutil.ReadFile(name)
200         if err != nil {
201                 return nil, false
202         }
203         return parseinfo(buf)
204 }
205
206 var zones []zonetime
207 var onceSetupZone sync.Once
208
209 func setupZone() {
210         // consult $TZ to find the time zone to use.
211         // no $TZ means use the system default /etc/localtime.
212         // $TZ="" means use UTC.
213         // $TZ="foo" means use /usr/share/zoneinfo/foo.
214
215         tz, err := os.Getenverror("TZ")
216         switch {
217         case err == os.ENOENV:
218                 zones, _ = readinfofile("/etc/localtime")
219         case len(tz) > 0:
220                 var ok bool
221                 zones, ok = readinfofile(zoneDir + tz)
222                 if !ok {
223                         zones, _ = readinfofile(zoneDir2 + tz)
224                 }
225         case len(tz) == 0:
226                 // do nothing: use UTC
227         }
228 }
229
230 // Look up the correct time zone (daylight savings or not) for the given unix time, in the current location.
231 func lookupTimezone(sec int64) (zone string, offset int) {
232         onceSetupZone.Do(setupZone)
233         if len(zones) == 0 {
234                 return "UTC", 0
235         }
236
237         // Binary search for entry with largest time <= sec
238         tz := zones
239         for len(tz) > 1 {
240                 m := len(tz) / 2
241                 if sec < int64(tz[m].time) {
242                         tz = tz[0:m]
243                 } else {
244                         tz = tz[m:]
245                 }
246         }
247         z := tz[0].zone
248         return z.name, z.utcoff
249 }
250
251 // lookupByName returns the time offset for the
252 // time zone with the given abbreviation. It only considers
253 // time zones that apply to the current system.
254 // For example, for a system configured as being in New York,
255 // it only recognizes "EST" and "EDT".
256 // For a system in San Francisco, "PST" and "PDT".
257 // For a system in Sydney, "EST" and "EDT", though they have
258 // different meanings than they do in New York.
259 func lookupByName(name string) (off int, found bool) {
260         onceSetupZone.Do(setupZone)
261         for _, z := range zones {
262                 if name == z.zone.name {
263                         return z.zone.utcoff, true
264                 }
265         }
266         return 0, false
267 }