OSDN Git Service

Update Go library to last weekly.
[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 // +build darwin freebsd linux openbsd
6
7 // Parse "zoneinfo" time zone file.
8 // This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others.
9 // See tzfile(5), http://en.wikipedia.org/wiki/Zoneinfo,
10 // and ftp://munnari.oz.au/pub/oldtz/
11
12 package time
13
14 import (
15         "io/ioutil"
16         "os"
17 )
18
19 const (
20         headerSize = 4 + 16 + 4*7
21 )
22
23 // Simple I/O interface to binary blob of data.
24 type data struct {
25         p     []byte
26         error bool
27 }
28
29 func (d *data) read(n int) []byte {
30         if len(d.p) < n {
31                 d.p = nil
32                 d.error = true
33                 return nil
34         }
35         p := d.p[0:n]
36         d.p = d.p[n:]
37         return p
38 }
39
40 func (d *data) big4() (n uint32, ok bool) {
41         p := d.read(4)
42         if len(p) < 4 {
43                 d.error = true
44                 return 0, false
45         }
46         return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]), true
47 }
48
49 func (d *data) byte() (n byte, ok bool) {
50         p := d.read(1)
51         if len(p) < 1 {
52                 d.error = true
53                 return 0, false
54         }
55         return p[0], true
56 }
57
58 // Make a string by stopping at the first NUL
59 func byteString(p []byte) string {
60         for i := 0; i < len(p); i++ {
61                 if p[i] == 0 {
62                         return string(p[0:i])
63                 }
64         }
65         return string(p)
66 }
67
68 func parseinfo(bytes []byte) (zt []zonetime, ok bool) {
69         d := data{bytes, false}
70
71         // 4-byte magic "TZif"
72         if magic := d.read(4); string(magic) != "TZif" {
73                 return nil, false
74         }
75
76         // 1-byte version, then 15 bytes of padding
77         var p []byte
78         if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' {
79                 return nil, false
80         }
81
82         // six big-endian 32-bit integers:
83         //      number of UTC/local indicators
84         //      number of standard/wall indicators
85         //      number of leap seconds
86         //      number of transition times
87         //      number of local time zones
88         //      number of characters of time zone abbrev strings
89         const (
90                 NUTCLocal = iota
91                 NStdWall
92                 NLeap
93                 NTime
94                 NZone
95                 NChar
96         )
97         var n [6]int
98         for i := 0; i < 6; i++ {
99                 nn, ok := d.big4()
100                 if !ok {
101                         return nil, false
102                 }
103                 n[i] = int(nn)
104         }
105
106         // Transition times.
107         txtimes := data{d.read(n[NTime] * 4), false}
108
109         // Time zone indices for transition times.
110         txzones := d.read(n[NTime])
111
112         // Zone info structures
113         zonedata := data{d.read(n[NZone] * 6), false}
114
115         // Time zone abbreviations.
116         abbrev := d.read(n[NChar])
117
118         // Leap-second time pairs
119         d.read(n[NLeap] * 8)
120
121         // Whether tx times associated with local time types
122         // are specified as standard time or wall time.
123         isstd := d.read(n[NStdWall])
124
125         // Whether tx times associated with local time types
126         // are specified as UTC or local time.
127         isutc := d.read(n[NUTCLocal])
128
129         if d.error { // ran out of data
130                 return nil, false
131         }
132
133         // If version == 2, the entire file repeats, this time using
134         // 8-byte ints for txtimes and leap seconds.
135         // We won't need those until 2106.
136
137         // Now we can build up a useful data structure.
138         // First the zone information.
139         //      utcoff[4] isdst[1] nameindex[1]
140         z := make([]zone, n[NZone])
141         for i := 0; i < len(z); i++ {
142                 var ok bool
143                 var n uint32
144                 if n, ok = zonedata.big4(); !ok {
145                         return nil, false
146                 }
147                 z[i].utcoff = int(n)
148                 var b byte
149                 if b, ok = zonedata.byte(); !ok {
150                         return nil, false
151                 }
152                 z[i].isdst = b != 0
153                 if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
154                         return nil, false
155                 }
156                 z[i].name = byteString(abbrev[b:])
157         }
158
159         // Now the transition time info.
160         zt = make([]zonetime, n[NTime])
161         for i := 0; i < len(zt); i++ {
162                 var ok bool
163                 var n uint32
164                 if n, ok = txtimes.big4(); !ok {
165                         return nil, false
166                 }
167                 zt[i].time = int32(n)
168                 if int(txzones[i]) >= len(z) {
169                         return nil, false
170                 }
171                 zt[i].zone = &z[txzones[i]]
172                 if i < len(isstd) {
173                         zt[i].isstd = isstd[i] != 0
174                 }
175                 if i < len(isutc) {
176                         zt[i].isutc = isutc[i] != 0
177                 }
178         }
179         return zt, true
180 }
181
182 func readinfofile(name string) ([]zonetime, bool) {
183         buf, err := ioutil.ReadFile(name)
184         if err != nil {
185                 return nil, false
186         }
187         return parseinfo(buf)
188 }
189
190 func setupTestingZone() {
191         os.Setenv("TZ", "America/Los_Angeles")
192         setupZone()
193 }
194
195 func setupZone() {
196         // consult $TZ to find the time zone to use.
197         // no $TZ means use the system default /etc/localtime.
198         // $TZ="" means use UTC.
199         // $TZ="foo" means use /usr/share/zoneinfo/foo.
200         // Many systems use /usr/share/zoneinfo, Solaris 2 has
201         // /usr/share/lib/zoneinfo, IRIX 6 has /usr/lib/locale/TZ.
202         zoneDirs := []string{"/usr/share/zoneinfo/",
203                 "/usr/share/lib/zoneinfo/",
204                 "/usr/lib/locale/TZ/"}
205
206         tz, err := os.Getenverror("TZ")
207         switch {
208         case err == os.ENOENV:
209                 zones, _ = readinfofile("/etc/localtime")
210         case len(tz) > 0:
211                 for _, zoneDir := range zoneDirs {
212                         var ok bool
213                         if zones, ok = readinfofile(zoneDir + tz); ok {
214                                 break
215                         }
216                 }
217         case len(tz) == 0:
218                 // do nothing: use UTC
219         }
220 }