PR middle-end/81768
[official-gcc.git] / libgo / go / time / zoneinfo_read.go
blob19cd40d847729711297c86468c9f9b73e8fd3864
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.
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/
10 package time
12 import "errors"
14 // Copies of io.Seek* constants to avoid importing "io":
15 const (
16 seekStart = 0
17 seekCurrent = 1
18 seekEnd = 2
21 // Simple I/O interface to binary blob of data.
22 type data struct {
23 p []byte
24 error bool
27 func (d *data) read(n int) []byte {
28 if len(d.p) < n {
29 d.p = nil
30 d.error = true
31 return nil
33 p := d.p[0:n]
34 d.p = d.p[n:]
35 return p
38 func (d *data) big4() (n uint32, ok bool) {
39 p := d.read(4)
40 if len(p) < 4 {
41 d.error = true
42 return 0, false
44 return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]), true
47 func (d *data) byte() (n byte, ok bool) {
48 p := d.read(1)
49 if len(p) < 1 {
50 d.error = true
51 return 0, false
53 return p[0], true
56 // Make a string by stopping at the first NUL
57 func byteString(p []byte) string {
58 for i := 0; i < len(p); i++ {
59 if p[i] == 0 {
60 return string(p[0:i])
63 return string(p)
66 var badData = errors.New("malformed time zone information")
68 func loadZoneData(bytes []byte) (l *Location, err error) {
69 d := data{bytes, false}
71 // 4-byte magic "TZif"
72 if magic := d.read(4); string(magic) != "TZif" {
73 return nil, badData
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' && p[0] != '3' {
79 return nil, badData
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
97 var n [6]int
98 for i := 0; i < 6; i++ {
99 nn, ok := d.big4()
100 if !ok {
101 return nil, badData
103 n[i] = int(nn)
106 // Transition times.
107 txtimes := data{d.read(n[NTime] * 4), false}
109 // Time zone indices for transition times.
110 txzones := d.read(n[NTime])
112 // Zone info structures
113 zonedata := data{d.read(n[NZone] * 6), false}
115 // Time zone abbreviations.
116 abbrev := d.read(n[NChar])
118 // Leap-second time pairs
119 d.read(n[NLeap] * 8)
121 // Whether tx times associated with local time types
122 // are specified as standard time or wall time.
123 isstd := d.read(n[NStdWall])
125 // Whether tx times associated with local time types
126 // are specified as UTC or local time.
127 isutc := d.read(n[NUTCLocal])
129 if d.error { // ran out of data
130 return nil, badData
133 // If version == 2 or 3, the entire file repeats, this time using
134 // 8-byte ints for txtimes and leap seconds.
135 // We won't need those until 2106.
137 // Now we can build up a useful data structure.
138 // First the zone information.
139 // utcoff[4] isdst[1] nameindex[1]
140 zone := make([]zone, n[NZone])
141 for i := range zone {
142 var ok bool
143 var n uint32
144 if n, ok = zonedata.big4(); !ok {
145 return nil, badData
147 zone[i].offset = int(int32(n))
148 var b byte
149 if b, ok = zonedata.byte(); !ok {
150 return nil, badData
152 zone[i].isDST = b != 0
153 if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
154 return nil, badData
156 zone[i].name = byteString(abbrev[b:])
159 // Now the transition time info.
160 tx := make([]zoneTrans, n[NTime])
161 for i := range tx {
162 var ok bool
163 var n uint32
164 if n, ok = txtimes.big4(); !ok {
165 return nil, badData
167 tx[i].when = int64(int32(n))
168 if int(txzones[i]) >= len(zone) {
169 return nil, badData
171 tx[i].index = txzones[i]
172 if i < len(isstd) {
173 tx[i].isstd = isstd[i] != 0
175 if i < len(isutc) {
176 tx[i].isutc = isutc[i] != 0
180 if len(tx) == 0 {
181 // Build fake transition to cover all time.
182 // This happens in fixed locations like "Etc/GMT0".
183 tx = append(tx, zoneTrans{when: alpha, index: 0})
186 // Committed to succeed.
187 l = &Location{zone: zone, tx: tx}
189 // Fill in the cache with information about right now,
190 // since that will be the most common lookup.
191 sec, _ := now()
192 for i := range tx {
193 if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) {
194 l.cacheStart = tx[i].when
195 l.cacheEnd = omega
196 if i+1 < len(tx) {
197 l.cacheEnd = tx[i+1].when
199 l.cacheZone = &l.zone[tx[i].index]
203 return l, nil
206 func loadZoneFile(dir, name string) (l *Location, err error) {
207 if len(dir) > 4 && dir[len(dir)-4:] == ".zip" {
208 return loadZoneZip(dir, name)
210 if dir != "" {
211 name = dir + "/" + name
213 buf, err := readFile(name)
214 if err != nil {
215 return
217 return loadZoneData(buf)
220 // There are 500+ zoneinfo files. Rather than distribute them all
221 // individually, we ship them in an uncompressed zip file.
222 // Used this way, the zip file format serves as a commonly readable
223 // container for the individual small files. We choose zip over tar
224 // because zip files have a contiguous table of contents, making
225 // individual file lookups faster, and because the per-file overhead
226 // in a zip file is considerably less than tar's 512 bytes.
228 // get4 returns the little-endian 32-bit value in b.
229 func get4(b []byte) int {
230 if len(b) < 4 {
231 return 0
233 return int(b[0]) | int(b[1])<<8 | int(b[2])<<16 | int(b[3])<<24
236 // get2 returns the little-endian 16-bit value in b.
237 func get2(b []byte) int {
238 if len(b) < 2 {
239 return 0
241 return int(b[0]) | int(b[1])<<8
244 func loadZoneZip(zipfile, name string) (l *Location, err error) {
245 fd, err := open(zipfile)
246 if err != nil {
247 return nil, errors.New("open " + zipfile + ": " + err.Error())
249 defer closefd(fd)
251 const (
252 zecheader = 0x06054b50
253 zcheader = 0x02014b50
254 ztailsize = 22
256 zheadersize = 30
257 zheader = 0x04034b50
260 buf := make([]byte, ztailsize)
261 if err := preadn(fd, buf, -ztailsize); err != nil || get4(buf) != zecheader {
262 return nil, errors.New("corrupt zip file " + zipfile)
264 n := get2(buf[10:])
265 size := get4(buf[12:])
266 off := get4(buf[16:])
268 buf = make([]byte, size)
269 if err := preadn(fd, buf, off); err != nil {
270 return nil, errors.New("corrupt zip file " + zipfile)
273 for i := 0; i < n; i++ {
274 // zip entry layout:
275 // 0 magic[4]
276 // 4 madevers[1]
277 // 5 madeos[1]
278 // 6 extvers[1]
279 // 7 extos[1]
280 // 8 flags[2]
281 // 10 meth[2]
282 // 12 modtime[2]
283 // 14 moddate[2]
284 // 16 crc[4]
285 // 20 csize[4]
286 // 24 uncsize[4]
287 // 28 namelen[2]
288 // 30 xlen[2]
289 // 32 fclen[2]
290 // 34 disknum[2]
291 // 36 iattr[2]
292 // 38 eattr[4]
293 // 42 off[4]
294 // 46 name[namelen]
295 // 46+namelen+xlen+fclen - next header
297 if get4(buf) != zcheader {
298 break
300 meth := get2(buf[10:])
301 size := get4(buf[24:])
302 namelen := get2(buf[28:])
303 xlen := get2(buf[30:])
304 fclen := get2(buf[32:])
305 off := get4(buf[42:])
306 zname := buf[46 : 46+namelen]
307 buf = buf[46+namelen+xlen+fclen:]
308 if string(zname) != name {
309 continue
311 if meth != 0 {
312 return nil, errors.New("unsupported compression for " + name + " in " + zipfile)
315 // zip per-file header layout:
316 // 0 magic[4]
317 // 4 extvers[1]
318 // 5 extos[1]
319 // 6 flags[2]
320 // 8 meth[2]
321 // 10 modtime[2]
322 // 12 moddate[2]
323 // 14 crc[4]
324 // 18 csize[4]
325 // 22 uncsize[4]
326 // 26 namelen[2]
327 // 28 xlen[2]
328 // 30 name[namelen]
329 // 30+namelen+xlen - file data
331 buf = make([]byte, zheadersize+namelen)
332 if err := preadn(fd, buf, off); err != nil ||
333 get4(buf) != zheader ||
334 get2(buf[8:]) != meth ||
335 get2(buf[26:]) != namelen ||
336 string(buf[30:30+namelen]) != name {
337 return nil, errors.New("corrupt zip file " + zipfile)
339 xlen = get2(buf[28:])
341 buf = make([]byte, size)
342 if err := preadn(fd, buf, off+30+namelen+xlen); err != nil {
343 return nil, errors.New("corrupt zip file " + zipfile)
346 return loadZoneData(buf)
349 return nil, errors.New("cannot find " + name + " in zip file " + zipfile)