Source file src/time/tzdata/tzdata.go

     1  // Copyright 2020 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  //go:generate go run generate_zipdata.go
     6  
     7  // Package tzdata provides an embedded copy of the timezone database.
     8  // If this package is imported anywhere in the program, then if
     9  // the time package cannot find tzdata files on the system,
    10  // it will use this embedded information.
    11  //
    12  // Importing this package will increase the size of a program by about
    13  // 450 KB.
    14  //
    15  // This package should normally be imported by a program's main package,
    16  // not by a library. Libraries normally shouldn't decide whether to
    17  // include the timezone database in a program.
    18  //
    19  // This package will be automatically imported if you build with
    20  // -tags timetzdata.
    21  package tzdata
    22  
    23  // The test for this package is time/tzdata_test.go.
    24  
    25  import (
    26  	"errors"
    27  	"syscall"
    28  	_ "unsafe" // for go:linkname
    29  )
    30  
    31  // registerLoadFromEmbeddedTZData is defined in package time.
    32  //go:linkname registerLoadFromEmbeddedTZData time.registerLoadFromEmbeddedTZData
    33  func registerLoadFromEmbeddedTZData(func(string) (string, error))
    34  
    35  func init() {
    36  	registerLoadFromEmbeddedTZData(loadFromEmbeddedTZData)
    37  }
    38  
    39  // get4s returns the little-endian 32-bit value at the start of s.
    40  func get4s(s string) int {
    41  	if len(s) < 4 {
    42  		return 0
    43  	}
    44  	return int(s[0]) | int(s[1])<<8 | int(s[2])<<16 | int(s[3])<<24
    45  }
    46  
    47  // get2s returns the little-endian 16-bit value at the start of s.
    48  func get2s(s string) int {
    49  	if len(s) < 2 {
    50  		return 0
    51  	}
    52  	return int(s[0]) | int(s[1])<<8
    53  }
    54  
    55  // loadFromEmbeddedTZData returns the contents of the file with the given
    56  // name in an uncompressed zip file, where the contents of the file can
    57  // be found in embeddedTzdata.
    58  // This is similar to time.loadTzinfoFromZip.
    59  func loadFromEmbeddedTZData(name string) (string, error) {
    60  	const (
    61  		zecheader = 0x06054b50
    62  		zcheader  = 0x02014b50
    63  		ztailsize = 22
    64  
    65  		zheadersize = 30
    66  		zheader     = 0x04034b50
    67  	)
    68  
    69  	z := zipdata
    70  
    71  	idx := len(z) - ztailsize
    72  	n := get2s(z[idx+10:])
    73  	idx = get4s(z[idx+16:])
    74  
    75  	for i := 0; i < n; i++ {
    76  		// See time.loadTzinfoFromZip for zip entry layout.
    77  		if get4s(z[idx:]) != zcheader {
    78  			break
    79  		}
    80  		meth := get2s(z[idx+10:])
    81  		size := get4s(z[idx+24:])
    82  		namelen := get2s(z[idx+28:])
    83  		xlen := get2s(z[idx+30:])
    84  		fclen := get2s(z[idx+32:])
    85  		off := get4s(z[idx+42:])
    86  		zname := z[idx+46 : idx+46+namelen]
    87  		idx += 46 + namelen + xlen + fclen
    88  		if zname != name {
    89  			continue
    90  		}
    91  		if meth != 0 {
    92  			return "", errors.New("unsupported compression for " + name + " in embedded tzdata")
    93  		}
    94  
    95  		// See time.loadTzinfoFromZip for zip per-file header layout.
    96  		idx = off
    97  		if get4s(z[idx:]) != zheader ||
    98  			get2s(z[idx+8:]) != meth ||
    99  			get2s(z[idx+26:]) != namelen ||
   100  			z[idx+30:idx+30+namelen] != name {
   101  			return "", errors.New("corrupt embedded tzdata")
   102  		}
   103  		xlen = get2s(z[idx+28:])
   104  		idx += 30 + namelen + xlen
   105  		return z[idx : idx+size], nil
   106  	}
   107  
   108  	return "", syscall.ENOENT
   109  }
   110  

View as plain text