Source file
src/time/zoneinfo_read.go
Documentation: time
1
2
3
4
5
6
7
8
9
10 package time
11
12 import (
13 "errors"
14 "syscall"
15 )
16
17
18
19
20 const maxFileSize = 10 << 20
21
22 type fileSizeError string
23
24 func (f fileSizeError) Error() string {
25 return "time: file " + string(f) + " is too large"
26 }
27
28
29 const (
30 seekStart = 0
31 seekCurrent = 1
32 seekEnd = 2
33 )
34
35
36 type dataIO struct {
37 p []byte
38 error bool
39 }
40
41 func (d *dataIO) read(n int) []byte {
42 if len(d.p) < n {
43 d.p = nil
44 d.error = true
45 return nil
46 }
47 p := d.p[0:n]
48 d.p = d.p[n:]
49 return p
50 }
51
52 func (d *dataIO) big4() (n uint32, ok bool) {
53 p := d.read(4)
54 if len(p) < 4 {
55 d.error = true
56 return 0, false
57 }
58 return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]), true
59 }
60
61 func (d *dataIO) byte() (n byte, ok bool) {
62 p := d.read(1)
63 if len(p) < 1 {
64 d.error = true
65 return 0, false
66 }
67 return p[0], true
68 }
69
70
71 func byteString(p []byte) string {
72 for i := 0; i < len(p); i++ {
73 if p[i] == 0 {
74 return string(p[0:i])
75 }
76 }
77 return string(p)
78 }
79
80 var badData = errors.New("malformed time zone information")
81
82
83
84
85
86 func LoadLocationFromTZData(name string, data []byte) (*Location, error) {
87 d := dataIO{data, false}
88
89
90 if magic := d.read(4); string(magic) != "TZif" {
91 return nil, badData
92 }
93
94
95 var p []byte
96 if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' && p[0] != '3' {
97 return nil, badData
98 }
99
100
101
102
103
104
105
106
107 const (
108 NUTCLocal = iota
109 NStdWall
110 NLeap
111 NTime
112 NZone
113 NChar
114 )
115 var n [6]int
116 for i := 0; i < 6; i++ {
117 nn, ok := d.big4()
118 if !ok {
119 return nil, badData
120 }
121 n[i] = int(nn)
122 }
123
124
125 txtimes := dataIO{d.read(n[NTime] * 4), false}
126
127
128 txzones := d.read(n[NTime])
129
130
131 zonedata := dataIO{d.read(n[NZone] * 6), false}
132
133
134 abbrev := d.read(n[NChar])
135
136
137 d.read(n[NLeap] * 8)
138
139
140
141 isstd := d.read(n[NStdWall])
142
143
144
145 isutc := d.read(n[NUTCLocal])
146
147 if d.error {
148 return nil, badData
149 }
150
151
152
153
154
155
156
157
158 zone := make([]zone, n[NZone])
159 for i := range zone {
160 var ok bool
161 var n uint32
162 if n, ok = zonedata.big4(); !ok {
163 return nil, badData
164 }
165 zone[i].offset = int(int32(n))
166 var b byte
167 if b, ok = zonedata.byte(); !ok {
168 return nil, badData
169 }
170 zone[i].isDST = b != 0
171 if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
172 return nil, badData
173 }
174 zone[i].name = byteString(abbrev[b:])
175 }
176
177
178 tx := make([]zoneTrans, n[NTime])
179 for i := range tx {
180 var ok bool
181 var n uint32
182 if n, ok = txtimes.big4(); !ok {
183 return nil, badData
184 }
185 tx[i].when = int64(int32(n))
186 if int(txzones[i]) >= len(zone) {
187 return nil, badData
188 }
189 tx[i].index = txzones[i]
190 if i < len(isstd) {
191 tx[i].isstd = isstd[i] != 0
192 }
193 if i < len(isutc) {
194 tx[i].isutc = isutc[i] != 0
195 }
196 }
197
198 if len(tx) == 0 {
199
200
201 tx = append(tx, zoneTrans{when: alpha, index: 0})
202 }
203
204
205 l := &Location{zone: zone, tx: tx, name: name}
206
207
208
209 sec, _, _ := now()
210 for i := range tx {
211 if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) {
212 l.cacheStart = tx[i].when
213 l.cacheEnd = omega
214 if i+1 < len(tx) {
215 l.cacheEnd = tx[i+1].when
216 }
217 l.cacheZone = &l.zone[tx[i].index]
218 }
219 }
220
221 return l, nil
222 }
223
224
225
226 func loadTzinfoFromDirOrZip(dir, name string) ([]byte, error) {
227 if len(dir) > 4 && dir[len(dir)-4:] == ".zip" {
228 return loadTzinfoFromZip(dir, name)
229 }
230 if dir != "" {
231 name = dir + "/" + name
232 }
233 return readFile(name)
234 }
235
236
237
238
239
240
241
242
243
244
245 func get4(b []byte) int {
246 if len(b) < 4 {
247 return 0
248 }
249 return int(b[0]) | int(b[1])<<8 | int(b[2])<<16 | int(b[3])<<24
250 }
251
252
253 func get2(b []byte) int {
254 if len(b) < 2 {
255 return 0
256 }
257 return int(b[0]) | int(b[1])<<8
258 }
259
260
261
262 func loadTzinfoFromZip(zipfile, name string) ([]byte, error) {
263 fd, err := open(zipfile)
264 if err != nil {
265 return nil, errors.New("open " + zipfile + ": " + err.Error())
266 }
267 defer closefd(fd)
268
269 const (
270 zecheader = 0x06054b50
271 zcheader = 0x02014b50
272 ztailsize = 22
273
274 zheadersize = 30
275 zheader = 0x04034b50
276 )
277
278 buf := make([]byte, ztailsize)
279 if err := preadn(fd, buf, -ztailsize); err != nil || get4(buf) != zecheader {
280 return nil, errors.New("corrupt zip file " + zipfile)
281 }
282 n := get2(buf[10:])
283 size := get4(buf[12:])
284 off := get4(buf[16:])
285
286 buf = make([]byte, size)
287 if err := preadn(fd, buf, off); err != nil {
288 return nil, errors.New("corrupt zip file " + zipfile)
289 }
290
291 for i := 0; i < n; i++ {
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315 if get4(buf) != zcheader {
316 break
317 }
318 meth := get2(buf[10:])
319 size := get4(buf[24:])
320 namelen := get2(buf[28:])
321 xlen := get2(buf[30:])
322 fclen := get2(buf[32:])
323 off := get4(buf[42:])
324 zname := buf[46 : 46+namelen]
325 buf = buf[46+namelen+xlen+fclen:]
326 if string(zname) != name {
327 continue
328 }
329 if meth != 0 {
330 return nil, errors.New("unsupported compression for " + name + " in " + zipfile)
331 }
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349 buf = make([]byte, zheadersize+namelen)
350 if err := preadn(fd, buf, off); err != nil ||
351 get4(buf) != zheader ||
352 get2(buf[8:]) != meth ||
353 get2(buf[26:]) != namelen ||
354 string(buf[30:30+namelen]) != name {
355 return nil, errors.New("corrupt zip file " + zipfile)
356 }
357 xlen = get2(buf[28:])
358
359 buf = make([]byte, size)
360 if err := preadn(fd, buf, off+30+namelen+xlen); err != nil {
361 return nil, errors.New("corrupt zip file " + zipfile)
362 }
363
364 return buf, nil
365 }
366
367 return nil, errors.New("cannot find " + name + " in zip file " + zipfile)
368 }
369
370
371
372
373 var loadTzinfoFromTzdata func(file, name string) ([]byte, error)
374
375
376
377
378
379 func loadTzinfo(name string, source string) ([]byte, error) {
380 if len(source) >= 6 && source[len(source)-6:] == "tzdata" {
381 return loadTzinfoFromTzdata(source, name)
382 }
383 return loadTzinfoFromDirOrZip(source, name)
384 }
385
386
387
388
389
390 func loadLocation(name string, sources []string) (z *Location, firstErr error) {
391 for _, source := range sources {
392 var zoneData, err = loadTzinfo(name, source)
393 if err == nil {
394 if z, err = LoadLocationFromTZData(name, zoneData); err == nil {
395 return z, nil
396 }
397 }
398 if firstErr == nil && err != syscall.ENOENT {
399 firstErr = err
400 }
401 }
402 if firstErr != nil {
403 return nil, firstErr
404 }
405 return nil, errors.New("unknown time zone " + name)
406 }
407
408
409
410
411
412 func readFile(name string) ([]byte, error) {
413 f, err := open(name)
414 if err != nil {
415 return nil, err
416 }
417 defer closefd(f)
418 var (
419 buf [4096]byte
420 ret []byte
421 n int
422 )
423 for {
424 n, err = read(f, buf[:])
425 if n > 0 {
426 ret = append(ret, buf[:n]...)
427 }
428 if n == 0 || err != nil {
429 break
430 }
431 if len(ret) > maxFileSize {
432 return nil, fileSizeError(name)
433 }
434 }
435 return ret, err
436 }
437
View as plain text