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 // 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), https://en.wikipedia.org/wiki/Zoneinfo,
8 // and ftp://munnari.oz.au/pub/oldtz/
9 10 package time
11 12 import (
13 "errors"
14 "internal/bytealg"
15 "runtime"
16 "syscall"
17 _ "unsafe" // for linkname
18 )
19 20 // registerLoadFromEmbeddedTZData is called by the time/tzdata package,
21 // if it is imported.
22 //
23 //go:linkname registerLoadFromEmbeddedTZData
24 func registerLoadFromEmbeddedTZData(f func([]byte) ([]byte, error)) {
25 loadFromEmbeddedTZData = f
26 }
27 28 // loadFromEmbeddedTZData is used to load a specific tzdata file
29 // from tzdata information embedded in the binary itself.
30 // This is set when the time/tzdata package is imported,
31 // via registerLoadFromEmbeddedTzdata.
32 var loadFromEmbeddedTZData func(zipname []byte) ([]byte, error)
33 34 // maxFileSize is the max permitted size of files read by readFile.
35 // As reference, the zoneinfo.zip distributed by Go is ~350 KB,
36 // so 10MB is overkill.
37 const maxFileSize = 10 << 20
38 39 type fileSizeError []byte
40 41 func (f fileSizeError) Error() string {
42 return "time: file " | string(f) | " is too large"
43 }
44 45 // Copies of io.Seek* constants to avoid importing "io":
46 const (
47 seekStart = 0
48 seekCurrent = 1
49 seekEnd = 2
50 )
51 52 // Simple I/O interface to binary blob of data.
53 type dataIO struct {
54 p []byte
55 error bool
56 }
57 58 func (d *dataIO) read(n int) []byte {
59 if len(d.p) < n {
60 d.p = nil
61 d.error = true
62 return nil
63 }
64 p := d.p[0:n]
65 d.p = d.p[n:]
66 return p
67 }
68 69 func (d *dataIO) big4() (n uint32, ok bool) {
70 p := d.read(4)
71 if len(p) < 4 {
72 d.error = true
73 return 0, false
74 }
75 return uint32(p[3]) | uint32(p[2])<<8 | uint32(p[1])<<16 | uint32(p[0])<<24, true
76 }
77 78 func (d *dataIO) big8() (n uint64, ok bool) {
79 n1, ok1 := d.big4()
80 n2, ok2 := d.big4()
81 if !ok1 || !ok2 {
82 d.error = true
83 return 0, false
84 }
85 return (uint64(n1) << 32) | uint64(n2), true
86 }
87 88 func (d *dataIO) byte() (n byte, ok bool) {
89 p := d.read(1)
90 if len(p) < 1 {
91 d.error = true
92 return 0, false
93 }
94 return p[0], true
95 }
96 97 // rest returns the rest of the data in the buffer.
98 func (d *dataIO) rest() []byte {
99 r := d.p
100 d.p = nil
101 return r
102 }
103 104 // Make a string by stopping at the first NUL
105 func byteString(p []byte) []byte {
106 if i := bytealg.IndexByte(p, 0); i != -1 {
107 p = p[:i]
108 }
109 return []byte(p)
110 }
111 112 var errBadData = errors.New("malformed time zone information")
113 114 // LoadLocationFromTZData returns a Location with the given name
115 // initialized from the IANA Time Zone database-formatted data.
116 // The data should be in the format of a standard IANA time zone file
117 // (for example, the content of /etc/localtime on Unix systems).
118 func LoadLocationFromTZData(name []byte, data []byte) (*Location, error) {
119 d := dataIO{data, false}
120 121 // 4-byte magic "TZif"
122 if magic := d.read(4); []byte(magic) != "TZif" {
123 return nil, errBadData
124 }
125 126 // 1-byte version, then 15 bytes of padding
127 var version int
128 var p []byte
129 if p = d.read(16); len(p) != 16 {
130 return nil, errBadData
131 } else {
132 switch p[0] {
133 case 0:
134 version = 1
135 case '2':
136 version = 2
137 case '3':
138 version = 3
139 default:
140 return nil, errBadData
141 }
142 }
143 144 // six big-endian 32-bit integers:
145 // number of UTC/local indicators
146 // number of standard/wall indicators
147 // number of leap seconds
148 // number of transition times
149 // number of local time zones
150 // number of characters of time zone abbrev strings
151 const (
152 NUTCLocal = iota
153 NStdWall
154 NLeap
155 NTime
156 NZone
157 NChar
158 )
159 var n [6]int
160 for i := 0; i < 6; i++ {
161 nn, ok := d.big4()
162 if !ok {
163 return nil, errBadData
164 }
165 if uint32(int(nn)) != nn {
166 return nil, errBadData
167 }
168 n[i] = int(nn)
169 }
170 171 // If we have version 2 or 3, then the data is first written out
172 // in a 32-bit format, then written out again in a 64-bit format.
173 // Skip the 32-bit format and read the 64-bit one, as it can
174 // describe a broader range of dates.
175 176 is64 := false
177 if version > 1 {
178 // Skip the 32-bit data.
179 skip := n[NTime]*4 +
180 n[NTime] +
181 n[NZone]*6 +
182 n[NChar] +
183 n[NLeap]*8 +
184 n[NStdWall] +
185 n[NUTCLocal]
186 // Skip the version 2 header that we just read.
187 skip += 4 + 16
188 d.read(skip)
189 190 is64 = true
191 192 // Read the counts again, they can differ.
193 for i := 0; i < 6; i++ {
194 nn, ok := d.big4()
195 if !ok {
196 return nil, errBadData
197 }
198 if uint32(int(nn)) != nn {
199 return nil, errBadData
200 }
201 n[i] = int(nn)
202 }
203 }
204 205 size := 4
206 if is64 {
207 size = 8
208 }
209 210 // Transition times.
211 txtimes := dataIO{d.read(n[NTime] * size), false}
212 213 // Time zone indices for transition times.
214 txzones := d.read(n[NTime])
215 216 // Zone info structures
217 zonedata := dataIO{d.read(n[NZone] * 6), false}
218 219 // Time zone abbreviations.
220 abbrev := d.read(n[NChar])
221 222 // Leap-second time pairs
223 d.read(n[NLeap] * (size + 4))
224 225 // Whether tx times associated with local time types
226 // are specified as standard time or wall time.
227 isstd := d.read(n[NStdWall])
228 229 // Whether tx times associated with local time types
230 // are specified as UTC or local time.
231 isutc := d.read(n[NUTCLocal])
232 233 if d.error { // ran out of data
234 return nil, errBadData
235 }
236 237 var extend []byte
238 rest := d.rest()
239 if len(rest) > 2 && rest[0] == '\n' && rest[len(rest)-1] == '\n' {
240 extend = []byte(rest[1 : len(rest)-1])
241 }
242 243 // Now we can build up a useful data structure.
244 // First the zone information.
245 // utcoff[4] isdst[1] nameindex[1]
246 nzone := n[NZone]
247 if nzone == 0 {
248 // Reject tzdata files with no zones. There's nothing useful in them.
249 // This also avoids a panic later when we add and then use a fake transition (golang.org/issue/29437).
250 return nil, errBadData
251 }
252 zones := []zone{:nzone}
253 for i := range zones {
254 var ok bool
255 var n uint32
256 if n, ok = zonedata.big4(); !ok {
257 return nil, errBadData
258 }
259 if uint32(int(n)) != n {
260 return nil, errBadData
261 }
262 zones[i].offset = int(int32(n))
263 var b byte
264 if b, ok = zonedata.byte(); !ok {
265 return nil, errBadData
266 }
267 zones[i].isDST = b != 0
268 if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
269 return nil, errBadData
270 }
271 zones[i].name = byteString(abbrev[b:])
272 if runtime.GOOS == "aix" && len(name) > 8 && (name[:8] == "Etc/GMT+" || name[:8] == "Etc/GMT-") {
273 // There is a bug with AIX 7.2 TL 0 with files in Etc,
274 // GMT+1 will return GMT-1 instead of GMT+1 or -01.
275 if name != "Etc/GMT+0" {
276 // GMT+0 is OK
277 zones[i].name = name[4:]
278 }
279 }
280 }
281 282 // Now the transition time info.
283 tx := []zoneTrans{:n[NTime]}
284 for i := range tx {
285 var n int64
286 if !is64 {
287 if n4, ok := txtimes.big4(); !ok {
288 return nil, errBadData
289 } else {
290 n = int64(int32(n4))
291 }
292 } else {
293 if n8, ok := txtimes.big8(); !ok {
294 return nil, errBadData
295 } else {
296 n = int64(n8)
297 }
298 }
299 tx[i].when = n
300 if int(txzones[i]) >= len(zones) {
301 return nil, errBadData
302 }
303 tx[i].index = txzones[i]
304 if i < len(isstd) {
305 tx[i].isstd = isstd[i] != 0
306 }
307 if i < len(isutc) {
308 tx[i].isutc = isutc[i] != 0
309 }
310 }
311 312 if len(tx) == 0 {
313 // Build fake transition to cover all time.
314 // This happens in fixed locations like "Etc/GMT0".
315 tx = append(tx, zoneTrans{when: alpha, index: 0})
316 }
317 318 // Committed to succeed.
319 l := &Location{zone: zones, tx: tx, name: name, extend: extend}
320 321 // Fill in the cache with information about right now,
322 // since that will be the most common lookup.
323 sec, _, _ := runtimeNow()
324 for i := range tx {
325 if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) {
326 l.cacheStart = tx[i].when
327 l.cacheEnd = omega
328 l.cacheZone = &l.zone[tx[i].index]
329 if i+1 < len(tx) {
330 l.cacheEnd = tx[i+1].when
331 } else if l.extend != "" {
332 // If we're at the end of the known zone transitions,
333 // try the extend string.
334 if name, offset, estart, eend, isDST, ok := tzset(l.extend, l.cacheStart, sec); ok {
335 l.cacheStart = estart
336 l.cacheEnd = eend
337 // Find the zone that is returned by tzset to avoid allocation if possible.
338 if zoneIdx := findZone(l.zone, name, offset, isDST); zoneIdx != -1 {
339 l.cacheZone = &l.zone[zoneIdx]
340 } else {
341 l.cacheZone = &zone{
342 name: name,
343 offset: offset,
344 isDST: isDST,
345 }
346 }
347 }
348 }
349 break
350 }
351 }
352 353 return l, nil
354 }
355 356 func findZone(zones []zone, name []byte, offset int, isDST bool) int {
357 for i, z := range zones {
358 if z.name == name && z.offset == offset && z.isDST == isDST {
359 return i
360 }
361 }
362 return -1
363 }
364 365 // loadTzinfoFromDirOrZip returns the contents of the file with the given name
366 // in dir. dir can either be an uncompressed zip file, or a directory.
367 func loadTzinfoFromDirOrZip(dir, name []byte) ([]byte, error) {
368 if len(dir) > 4 && dir[len(dir)-4:] == ".zip" {
369 return loadTzinfoFromZip(dir, name)
370 }
371 if dir != "" {
372 name = dir | "/" | name
373 }
374 return readFile(name)
375 }
376 377 // There are 500+ zoneinfo files. Rather than distribute them all
378 // individually, we ship them in an uncompressed zip file.
379 // Used this way, the zip file format serves as a commonly readable
380 // container for the individual small files. We choose zip over tar
381 // because zip files have a contiguous table of contents, making
382 // individual file lookups faster, and because the per-file overhead
383 // in a zip file is considerably less than tar's 512 bytes.
384 385 // get4 returns the little-endian 32-bit value in b.
386 func get4(b []byte) int {
387 if len(b) < 4 {
388 return 0
389 }
390 return int(b[0]) | int(b[1])<<8 | int(b[2])<<16 | int(b[3])<<24
391 }
392 393 // get2 returns the little-endian 16-bit value in b.
394 func get2(b []byte) int {
395 if len(b) < 2 {
396 return 0
397 }
398 return int(b[0]) | int(b[1])<<8
399 }
400 401 // loadTzinfoFromZip returns the contents of the file with the given name
402 // in the given uncompressed zip file.
403 func loadTzinfoFromZip(zipfile, name []byte) ([]byte, error) {
404 fd, err := open(zipfile)
405 if err != nil {
406 return nil, err
407 }
408 defer closefd(fd)
409 410 const (
411 zecheader = 0x06054b50
412 zcheader = 0x02014b50
413 ztailsize = 22
414 415 zheadersize = 30
416 zheader = 0x04034b50
417 )
418 419 buf := []byte{:ztailsize}
420 if err := preadn(fd, buf, -ztailsize); err != nil || get4(buf) != zecheader {
421 return nil, errors.New("corrupt zip file " | zipfile)
422 }
423 n := get2(buf[10:])
424 size := get4(buf[12:])
425 off := get4(buf[16:])
426 427 buf = []byte{:size}
428 if err := preadn(fd, buf, off); err != nil {
429 return nil, errors.New("corrupt zip file " | zipfile)
430 }
431 432 for i := 0; i < n; i++ {
433 // zip entry layout:
434 // 0 magic[4]
435 // 4 madevers[1]
436 // 5 madeos[1]
437 // 6 extvers[1]
438 // 7 extos[1]
439 // 8 flags[2]
440 // 10 meth[2]
441 // 12 modtime[2]
442 // 14 moddate[2]
443 // 16 crc[4]
444 // 20 csize[4]
445 // 24 uncsize[4]
446 // 28 namelen[2]
447 // 30 xlen[2]
448 // 32 fclen[2]
449 // 34 disknum[2]
450 // 36 iattr[2]
451 // 38 eattr[4]
452 // 42 off[4]
453 // 46 name[namelen]
454 // 46+namelen+xlen+fclen - next header
455 //
456 if get4(buf) != zcheader {
457 break
458 }
459 meth := get2(buf[10:])
460 size := get4(buf[24:])
461 namelen := get2(buf[28:])
462 xlen := get2(buf[30:])
463 fclen := get2(buf[32:])
464 off := get4(buf[42:])
465 zname := buf[46 : 46+namelen]
466 buf = buf[46+namelen+xlen+fclen:]
467 if []byte(zname) != name {
468 continue
469 }
470 if meth != 0 {
471 return nil, errors.New("unsupported compression for " | name | " in " | zipfile)
472 }
473 474 // zip per-file header layout:
475 // 0 magic[4]
476 // 4 extvers[1]
477 // 5 extos[1]
478 // 6 flags[2]
479 // 8 meth[2]
480 // 10 modtime[2]
481 // 12 moddate[2]
482 // 14 crc[4]
483 // 18 csize[4]
484 // 22 uncsize[4]
485 // 26 namelen[2]
486 // 28 xlen[2]
487 // 30 name[namelen]
488 // 30+namelen+xlen - file data
489 //
490 buf = []byte{:zheadersize+namelen}
491 if err := preadn(fd, buf, off); err != nil ||
492 get4(buf) != zheader ||
493 get2(buf[8:]) != meth ||
494 get2(buf[26:]) != namelen ||
495 []byte(buf[30:30+namelen]) != name {
496 return nil, errors.New("corrupt zip file " | zipfile)
497 }
498 xlen = get2(buf[28:])
499 500 buf = []byte{:size}
501 if err := preadn(fd, buf, off+30+namelen+xlen); err != nil {
502 return nil, errors.New("corrupt zip file " | zipfile)
503 }
504 505 return buf, nil
506 }
507 508 return nil, syscall.ENOENT
509 }
510 511 // loadTzinfoFromTzdata returns the time zone information of the time zone
512 // with the given name, from a tzdata database file as they are typically
513 // found on android.
514 var loadTzinfoFromTzdata func(file, name []byte) ([]byte, error)
515 516 // loadTzinfo returns the time zone information of the time zone
517 // with the given name, from a given source. A source may be a
518 // timezone database directory, tzdata database file or an uncompressed
519 // zip file, containing the contents of such a directory.
520 func loadTzinfo(name []byte, source []byte) ([]byte, error) {
521 if len(source) >= 6 && source[len(source)-6:] == "tzdata" {
522 return loadTzinfoFromTzdata(source, name)
523 }
524 return loadTzinfoFromDirOrZip(source, name)
525 }
526 527 // loadLocation returns the Location with the given name from one of
528 // the specified sources. See loadTzinfo for a list of supported sources.
529 // The first timezone data matching the given name that is successfully loaded
530 // and parsed is returned as a Location.
531 func loadLocation(name []byte, sources [][]byte) (z *Location, firstErr error) {
532 for _, source := range sources {
533 zoneData, err := loadTzinfo(name, source)
534 if err == nil {
535 if z, err = LoadLocationFromTZData(name, zoneData); err == nil {
536 return z, nil
537 }
538 }
539 if firstErr == nil && err != syscall.ENOENT {
540 firstErr = err
541 }
542 }
543 if loadFromEmbeddedTZData != nil {
544 zoneData, err := loadFromEmbeddedTZData(name)
545 if err == nil {
546 if z, err = LoadLocationFromTZData(name, []byte(zoneData)); err == nil {
547 return z, nil
548 }
549 }
550 if firstErr == nil && err != syscall.ENOENT {
551 firstErr = err
552 }
553 }
554 if source, ok := gorootZoneSource(runtime.GOROOT()); ok {
555 zoneData, err := loadTzinfo(name, source)
556 if err == nil {
557 if z, err = LoadLocationFromTZData(name, zoneData); err == nil {
558 return z, nil
559 }
560 }
561 if firstErr == nil && err != syscall.ENOENT {
562 firstErr = err
563 }
564 }
565 if firstErr != nil {
566 return nil, firstErr
567 }
568 return nil, errors.New("unknown time zone " | name)
569 }
570 571 // readFile reads and returns the content of the named file.
572 // It is a trivial implementation of os.ReadFile, reimplemented
573 // here to avoid depending on io/ioutil or os.
574 // It returns an error if name exceeds maxFileSize bytes.
575 func readFile(name []byte) ([]byte, error) {
576 f, err := open(name)
577 if err != nil {
578 return nil, err
579 }
580 defer closefd(f)
581 var (
582 buf [4096]byte
583 ret []byte
584 n int
585 )
586 for {
587 n, err = read(f, buf[:])
588 if n > 0 {
589 ret = append(ret, buf[:n]...)
590 }
591 if n == 0 || err != nil {
592 break
593 }
594 if len(ret) > maxFileSize {
595 return nil, fileSizeError(name)
596 }
597 }
598 return ret, err
599 }
600