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 zones := make([]zone, nzone)
251 for i := range zones {
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 zones[i].offset = int(int32(n))
261 var b byte
262 if b, ok = zonedata.byte(); !ok {
263 return nil, badData
264 }
265 zones[i].isDST = b != 0
266 if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
267 return nil, badData
268 }
269 zones[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 zones[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(zones) {
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: zones, 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 l.cacheZone = &l.zone[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, offset, estart, eend, isDST, ok := tzset(l.extend, l.cacheEnd, sec); ok {
333 l.cacheStart = estart
334 l.cacheEnd = eend
335
336 if zoneIdx := findZone(l.zone, name, offset, isDST); zoneIdx != -1 {
337 l.cacheZone = &l.zone[zoneIdx]
338 } else {
339 l.cacheZone = &zone{
340 name: name,
341 offset: offset,
342 isDST: isDST,
343 }
344 }
345 }
346 }
347 break
348 }
349 }
350
351 return l, nil
352 }
353
354 func findZone(zones []zone, name string, offset int, isDST bool) int {
355 for i, z := range zones {
356 if z.name == name && z.offset == offset && z.isDST == isDST {
357 return i
358 }
359 }
360 return -1
361 }
362
363
364
365 func loadTzinfoFromDirOrZip(dir, name string) ([]byte, error) {
366 if len(dir) > 4 && dir[len(dir)-4:] == ".zip" {
367 return loadTzinfoFromZip(dir, name)
368 }
369 if dir != "" {
370 name = dir + "/" + name
371 }
372 return readFile(name)
373 }
374
375
376
377
378
379
380
381
382
383
384 func get4(b []byte) int {
385 if len(b) < 4 {
386 return 0
387 }
388 return int(b[0]) | int(b[1])<<8 | int(b[2])<<16 | int(b[3])<<24
389 }
390
391
392 func get2(b []byte) int {
393 if len(b) < 2 {
394 return 0
395 }
396 return int(b[0]) | int(b[1])<<8
397 }
398
399
400
401 func loadTzinfoFromZip(zipfile, name string) ([]byte, error) {
402 fd, err := open(zipfile)
403 if err != nil {
404 return nil, err
405 }
406 defer closefd(fd)
407
408 const (
409 zecheader = 0x06054b50
410 zcheader = 0x02014b50
411 ztailsize = 22
412
413 zheadersize = 30
414 zheader = 0x04034b50
415 )
416
417 buf := make([]byte, ztailsize)
418 if err := preadn(fd, buf, -ztailsize); err != nil || get4(buf) != zecheader {
419 return nil, errors.New("corrupt zip file " + zipfile)
420 }
421 n := get2(buf[10:])
422 size := get4(buf[12:])
423 off := get4(buf[16:])
424
425 buf = make([]byte, size)
426 if err := preadn(fd, buf, off); err != nil {
427 return nil, errors.New("corrupt zip file " + zipfile)
428 }
429
430 for i := 0; i < n; i++ {
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454 if get4(buf) != zcheader {
455 break
456 }
457 meth := get2(buf[10:])
458 size := get4(buf[24:])
459 namelen := get2(buf[28:])
460 xlen := get2(buf[30:])
461 fclen := get2(buf[32:])
462 off := get4(buf[42:])
463 zname := buf[46 : 46+namelen]
464 buf = buf[46+namelen+xlen+fclen:]
465 if string(zname) != name {
466 continue
467 }
468 if meth != 0 {
469 return nil, errors.New("unsupported compression for " + name + " in " + zipfile)
470 }
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488 buf = make([]byte, zheadersize+namelen)
489 if err := preadn(fd, buf, off); err != nil ||
490 get4(buf) != zheader ||
491 get2(buf[8:]) != meth ||
492 get2(buf[26:]) != namelen ||
493 string(buf[30:30+namelen]) != name {
494 return nil, errors.New("corrupt zip file " + zipfile)
495 }
496 xlen = get2(buf[28:])
497
498 buf = make([]byte, size)
499 if err := preadn(fd, buf, off+30+namelen+xlen); err != nil {
500 return nil, errors.New("corrupt zip file " + zipfile)
501 }
502
503 return buf, nil
504 }
505
506 return nil, syscall.ENOENT
507 }
508
509
510
511
512 var loadTzinfoFromTzdata func(file, name string) ([]byte, error)
513
514
515
516
517
518 func loadTzinfo(name string, source string) ([]byte, error) {
519 if len(source) >= 6 && source[len(source)-6:] == "tzdata" {
520 return loadTzinfoFromTzdata(source, name)
521 }
522 return loadTzinfoFromDirOrZip(source, name)
523 }
524
525
526
527
528
529 func loadLocation(name string, sources []string) (z *Location, firstErr error) {
530 for _, source := range sources {
531 var zoneData, err = loadTzinfo(name, source)
532 if err == nil {
533 if z, err = LoadLocationFromTZData(name, zoneData); err == nil {
534 return z, nil
535 }
536 }
537 if firstErr == nil && err != syscall.ENOENT {
538 firstErr = err
539 }
540 }
541 if loadFromEmbeddedTZData != nil {
542 zonedata, err := loadFromEmbeddedTZData(name)
543 if err == nil {
544 if z, err = LoadLocationFromTZData(name, []byte(zonedata)); err == nil {
545 return z, nil
546 }
547 }
548 if firstErr == nil && err != syscall.ENOENT {
549 firstErr = err
550 }
551 }
552 if firstErr != nil {
553 return nil, firstErr
554 }
555 return nil, errors.New("unknown time zone " + name)
556 }
557
558
559
560
561
562 func readFile(name string) ([]byte, error) {
563 f, err := open(name)
564 if err != nil {
565 return nil, err
566 }
567 defer closefd(f)
568 var (
569 buf [4096]byte
570 ret []byte
571 n int
572 )
573 for {
574 n, err = read(f, buf[:])
575 if n > 0 {
576 ret = append(ret, buf[:n]...)
577 }
578 if n == 0 || err != nil {
579 break
580 }
581 if len(ret) > maxFileSize {
582 return nil, fileSizeError(name)
583 }
584 }
585 return ret, err
586 }
587
View as plain text