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