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 "runtime"
15 "syscall"
16 )
17
18
19
20 func registerLoadFromEmbeddedTZData(f func(string) (string, error)) {
21 loadFromEmbeddedTZData = f
22 }
23
24
25
26
27
28 var loadFromEmbeddedTZData func(zipname string) (string, error)
29
30
31
32
33 const maxFileSize = 10 << 20
34
35 type fileSizeError string
36
37 func (f fileSizeError) Error() string {
38 return "time: file " + string(f) + " is too large"
39 }
40
41
42 const (
43 seekStart = 0
44 seekCurrent = 1
45 seekEnd = 2
46 )
47
48
49 type dataIO struct {
50 p []byte
51 error bool
52 }
53
54 func (d *dataIO) read(n int) []byte {
55 if len(d.p) < n {
56 d.p = nil
57 d.error = true
58 return nil
59 }
60 p := d.p[0:n]
61 d.p = d.p[n:]
62 return p
63 }
64
65 func (d *dataIO) big4() (n uint32, ok bool) {
66 p := d.read(4)
67 if len(p) < 4 {
68 d.error = true
69 return 0, false
70 }
71 return uint32(p[3]) | uint32(p[2])<<8 | uint32(p[1])<<16 | uint32(p[0])<<24, true
72 }
73
74 func (d *dataIO) big8() (n uint64, ok bool) {
75 n1, ok1 := d.big4()
76 n2, ok2 := d.big4()
77 if !ok1 || !ok2 {
78 d.error = true
79 return 0, false
80 }
81 return (uint64(n1) << 32) | uint64(n2), true
82 }
83
84 func (d *dataIO) byte() (n byte, ok bool) {
85 p := d.read(1)
86 if len(p) < 1 {
87 d.error = true
88 return 0, false
89 }
90 return p[0], true
91 }
92
93
94 func (d *dataIO) rest() []byte {
95 r := d.p
96 d.p = nil
97 return r
98 }
99
100
101 func byteString(p []byte) string {
102 for i := 0; i < len(p); i++ {
103 if p[i] == 0 {
104 return string(p[0:i])
105 }
106 }
107 return string(p)
108 }
109
110 var badData = errors.New("malformed time zone information")
111
112
113
114
115
116 func LoadLocationFromTZData(name string, data []byte) (*Location, error) {
117 d := dataIO{data, false}
118
119
120 if magic := d.read(4); string(magic) != "TZif" {
121 return nil, badData
122 }
123
124
125 var version int
126 var p []byte
127 if p = d.read(16); len(p) != 16 {
128 return nil, badData
129 } else {
130 switch p[0] {
131 case 0:
132 version = 1
133 case '2':
134 version = 2
135 case '3':
136 version = 3
137 default:
138 return nil, badData
139 }
140 }
141
142
143
144
145
146
147
148
149 const (
150 NUTCLocal = iota
151 NStdWall
152 NLeap
153 NTime
154 NZone
155 NChar
156 )
157 var n [6]int
158 for i := 0; i < 6; i++ {
159 nn, ok := d.big4()
160 if !ok {
161 return nil, badData
162 }
163 if uint32(int(nn)) != nn {
164 return nil, badData
165 }
166 n[i] = int(nn)
167 }
168
169
170
171
172
173
174 is64 := false
175 if version > 1 {
176
177 skip := n[NTime]*4 +
178 n[NTime] +
179 n[NZone]*6 +
180 n[NChar] +
181 n[NLeap]*8 +
182 n[NStdWall] +
183 n[NUTCLocal]
184
185 skip += 4 + 16
186 d.read(skip)
187
188 is64 = true
189
190
191 for i := 0; i < 6; i++ {
192 nn, ok := d.big4()
193 if !ok {
194 return nil, badData
195 }
196 if uint32(int(nn)) != nn {
197 return nil, badData
198 }
199 n[i] = int(nn)
200 }
201 }
202
203 size := 4
204 if is64 {
205 size = 8
206 }
207
208
209 txtimes := dataIO{d.read(n[NTime] * size), false}
210
211
212 txzones := d.read(n[NTime])
213
214
215 zonedata := dataIO{d.read(n[NZone] * 6), false}
216
217
218 abbrev := d.read(n[NChar])
219
220
221 d.read(n[NLeap] * (size + 4))
222
223
224
225 isstd := d.read(n[NStdWall])
226
227
228
229 isutc := d.read(n[NUTCLocal])
230
231 if d.error {
232 return nil, badData
233 }
234
235 var extend string
236 rest := d.rest()
237 if len(rest) > 2 && rest[0] == '\n' && rest[len(rest)-1] == '\n' {
238 extend = string(rest[1 : len(rest)-1])
239 }
240
241
242
243
244 nzone := n[NZone]
245 if nzone == 0 {
246
247
248 return nil, badData
249 }
250 zone := make([]zone, nzone)
251 for i := range zone {
252 var ok bool
253 var n uint32
254 if n, ok = zonedata.big4(); !ok {
255 return nil, badData
256 }
257 if uint32(int(n)) != n {
258 return nil, badData
259 }
260 zone[i].offset = int(int32(n))
261 var b byte
262 if b, ok = zonedata.byte(); !ok {
263 return nil, badData
264 }
265 zone[i].isDST = b != 0
266 if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
267 return nil, badData
268 }
269 zone[i].name = byteString(abbrev[b:])
270 if runtime.GOOS == "aix" && len(name) > 8 && (name[:8] == "Etc/GMT+" || name[:8] == "Etc/GMT-") {
271
272
273 if name != "Etc/GMT+0" {
274
275 zone[i].name = name[4:]
276 }
277 }
278 }
279
280
281 tx := make([]zoneTrans, n[NTime])
282 for i := range tx {
283 var n int64
284 if !is64 {
285 if n4, ok := txtimes.big4(); !ok {
286 return nil, badData
287 } else {
288 n = int64(int32(n4))
289 }
290 } else {
291 if n8, ok := txtimes.big8(); !ok {
292 return nil, badData
293 } else {
294 n = int64(n8)
295 }
296 }
297 tx[i].when = n
298 if int(txzones[i]) >= len(zone) {
299 return nil, badData
300 }
301 tx[i].index = txzones[i]
302 if i < len(isstd) {
303 tx[i].isstd = isstd[i] != 0
304 }
305 if i < len(isutc) {
306 tx[i].isutc = isutc[i] != 0
307 }
308 }
309
310 if len(tx) == 0 {
311
312
313 tx = append(tx, zoneTrans{when: alpha, index: 0})
314 }
315
316
317 l := &Location{zone: zone, tx: tx, name: name, extend: extend}
318
319
320
321 sec, _, _ := now()
322 for i := range tx {
323 if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) {
324 l.cacheStart = tx[i].when
325 l.cacheEnd = omega
326 zoneIdx := tx[i].index
327 if i+1 < len(tx) {
328 l.cacheEnd = tx[i+1].when
329 } else if l.extend != "" {
330
331
332 if name, _, estart, eend, ok := tzset(l.extend, l.cacheEnd, sec); ok {
333 l.cacheStart = estart
334 l.cacheEnd = eend
335
336
337 for i, z := range l.zone {
338 if z.name == name {
339 zoneIdx = uint8(i)
340 break
341 }
342 }
343 }
344 }
345 l.cacheZone = &l.zone[zoneIdx]
346 break
347 }
348 }
349
350 return l, nil
351 }
352
353
354
355 func loadTzinfoFromDirOrZip(dir, name string) ([]byte, error) {
356 if len(dir) > 4 && dir[len(dir)-4:] == ".zip" {
357 return loadTzinfoFromZip(dir, name)
358 }
359 if dir != "" {
360 name = dir + "/" + name
361 }
362 return readFile(name)
363 }
364
365
366
367
368
369
370
371
372
373
374 func get4(b []byte) int {
375 if len(b) < 4 {
376 return 0
377 }
378 return int(b[0]) | int(b[1])<<8 | int(b[2])<<16 | int(b[3])<<24
379 }
380
381
382 func get2(b []byte) int {
383 if len(b) < 2 {
384 return 0
385 }
386 return int(b[0]) | int(b[1])<<8
387 }
388
389
390
391 func loadTzinfoFromZip(zipfile, name string) ([]byte, error) {
392 fd, err := open(zipfile)
393 if err != nil {
394 return nil, err
395 }
396 defer closefd(fd)
397
398 const (
399 zecheader = 0x06054b50
400 zcheader = 0x02014b50
401 ztailsize = 22
402
403 zheadersize = 30
404 zheader = 0x04034b50
405 )
406
407 buf := make([]byte, ztailsize)
408 if err := preadn(fd, buf, -ztailsize); err != nil || get4(buf) != zecheader {
409 return nil, errors.New("corrupt zip file " + zipfile)
410 }
411 n := get2(buf[10:])
412 size := get4(buf[12:])
413 off := get4(buf[16:])
414
415 buf = make([]byte, size)
416 if err := preadn(fd, buf, off); err != nil {
417 return nil, errors.New("corrupt zip file " + zipfile)
418 }
419
420 for i := 0; i < n; i++ {
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444 if get4(buf) != zcheader {
445 break
446 }
447 meth := get2(buf[10:])
448 size := get4(buf[24:])
449 namelen := get2(buf[28:])
450 xlen := get2(buf[30:])
451 fclen := get2(buf[32:])
452 off := get4(buf[42:])
453 zname := buf[46 : 46+namelen]
454 buf = buf[46+namelen+xlen+fclen:]
455 if string(zname) != name {
456 continue
457 }
458 if meth != 0 {
459 return nil, errors.New("unsupported compression for " + name + " in " + zipfile)
460 }
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478 buf = make([]byte, zheadersize+namelen)
479 if err := preadn(fd, buf, off); err != nil ||
480 get4(buf) != zheader ||
481 get2(buf[8:]) != meth ||
482 get2(buf[26:]) != namelen ||
483 string(buf[30:30+namelen]) != name {
484 return nil, errors.New("corrupt zip file " + zipfile)
485 }
486 xlen = get2(buf[28:])
487
488 buf = make([]byte, size)
489 if err := preadn(fd, buf, off+30+namelen+xlen); err != nil {
490 return nil, errors.New("corrupt zip file " + zipfile)
491 }
492
493 return buf, nil
494 }
495
496 return nil, syscall.ENOENT
497 }
498
499
500
501
502 var loadTzinfoFromTzdata func(file, name string) ([]byte, error)
503
504
505
506
507
508 func loadTzinfo(name string, source string) ([]byte, error) {
509 if len(source) >= 6 && source[len(source)-6:] == "tzdata" {
510 return loadTzinfoFromTzdata(source, name)
511 }
512 return loadTzinfoFromDirOrZip(source, name)
513 }
514
515
516
517
518
519 func loadLocation(name string, sources []string) (z *Location, firstErr error) {
520 for _, source := range sources {
521 var zoneData, err = loadTzinfo(name, source)
522 if err == nil {
523 if z, err = LoadLocationFromTZData(name, zoneData); err == nil {
524 return z, nil
525 }
526 }
527 if firstErr == nil && err != syscall.ENOENT {
528 firstErr = err
529 }
530 }
531 if loadFromEmbeddedTZData != nil {
532 zonedata, err := loadFromEmbeddedTZData(name)
533 if err == nil {
534 if z, err = LoadLocationFromTZData(name, []byte(zonedata)); err == nil {
535 return z, nil
536 }
537 }
538 if firstErr == nil && err != syscall.ENOENT {
539 firstErr = err
540 }
541 }
542 if firstErr != nil {
543 return nil, firstErr
544 }
545 return nil, errors.New("unknown time zone " + name)
546 }
547
548
549
550
551
552 func readFile(name string) ([]byte, error) {
553 f, err := open(name)
554 if err != nil {
555 return nil, err
556 }
557 defer closefd(f)
558 var (
559 buf [4096]byte
560 ret []byte
561 n int
562 )
563 for {
564 n, err = read(f, buf[:])
565 if n > 0 {
566 ret = append(ret, buf[:n]...)
567 }
568 if n == 0 || err != nil {
569 break
570 }
571 if len(ret) > maxFileSize {
572 return nil, fileSizeError(name)
573 }
574 }
575 return ret, err
576 }
577
View as plain text