-
Notifications
You must be signed in to change notification settings - Fork 17.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Importing the time/tzdata package will embed a copy of the IANA timezone database into the program. This will let the program work correctly when the timezone database is not available on the system. It will increase the size of the binary by about 800K. You can also build a program with -tags timetzdata to embed the timezone database in the program being built. Fixes #21881 Fixes #38013 Fixes #38017 Change-Id: Iffddee72a8f46c95fee3bcde43c142d6899d9246 Reviewed-on: https://go-review.googlesource.com/c/go/+/224588 Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Emmanuel Odeke <[email protected]> Reviewed-by: Tobias Klauser <[email protected]>
- Loading branch information
1 parent
300ed43
commit 6d63a74
Showing
9 changed files
with
13,386 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// Copyright 2020 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
// This file is used with build tag timetzdata to embed tzdata into | ||
// the binary. | ||
|
||
// +build timetzdata | ||
|
||
package time | ||
|
||
import _ "time/tzdata" |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// Copyright 2020 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
//go:generate go run generate_zipdata.go | ||
|
||
// Package tzdata provides an embedded copy of the timezone database. | ||
// If this package is imported anywhere in the program, then if | ||
// the time package cannot find tzdata files on the system, | ||
// it will use this embedded information. | ||
// | ||
// Importing this package will increase the size of a program by about | ||
// 800K. | ||
// | ||
// This package should normally be imported by a program's main package, | ||
// not by a library. Libraries normally shouldn't decide whether to | ||
// include the timezone database in a program. | ||
// | ||
// This package will be automatically imported if you build with | ||
// -tags timetzdata. | ||
package tzdata | ||
|
||
import ( | ||
"errors" | ||
"syscall" | ||
_ "unsafe" // for go:linkname | ||
) | ||
|
||
// registerLoadFromEmbeddedTZData is defined in package time. | ||
//go:linkname registerLoadFromEmbeddedTZData time.registerLoadFromEmbeddedTZData | ||
func registerLoadFromEmbeddedTZData(func(string) (string, error)) | ||
|
||
func init() { | ||
registerLoadFromEmbeddedTZData(loadFromEmbeddedTZData) | ||
} | ||
|
||
// get4s returns the little-endian 32-bit value at the start of s. | ||
func get4s(s string) int { | ||
if len(s) < 4 { | ||
return 0 | ||
} | ||
return int(s[0]) | int(s[1])<<8 | int(s[2])<<16 | int(s[3])<<24 | ||
} | ||
|
||
// get2s returns the little-endian 16-bit value at the start of s. | ||
func get2s(s string) int { | ||
if len(s) < 2 { | ||
return 0 | ||
} | ||
return int(s[0]) | int(s[1])<<8 | ||
} | ||
|
||
// loadFromEmbeddedTZData returns the contents of the file with the given | ||
// name in an uncompressed zip file, where the contents of the file can | ||
// be found in embeddedTzdata. | ||
// This is similar to time.loadTzinfoFromZip. | ||
func loadFromEmbeddedTZData(name string) (string, error) { | ||
const ( | ||
zecheader = 0x06054b50 | ||
zcheader = 0x02014b50 | ||
ztailsize = 22 | ||
|
||
zheadersize = 30 | ||
zheader = 0x04034b50 | ||
) | ||
|
||
z := zipdata | ||
|
||
idx := len(z) - ztailsize | ||
n := get2s(z[idx+10:]) | ||
idx = get4s(z[idx+16:]) | ||
|
||
for i := 0; i < n; i++ { | ||
// See time.loadTzinfoFromZip for zip entry layout. | ||
if get4s(z[idx:]) != zcheader { | ||
break | ||
} | ||
meth := get2s(z[idx+10:]) | ||
size := get4s(z[idx+24:]) | ||
namelen := get2s(z[idx+28:]) | ||
xlen := get2s(z[idx+30:]) | ||
fclen := get2s(z[idx+32:]) | ||
off := get4s(z[idx+42:]) | ||
zname := z[idx+46 : idx+46+namelen] | ||
idx += 46 + namelen + xlen + fclen | ||
if zname != name { | ||
continue | ||
} | ||
if meth != 0 { | ||
return "", errors.New("unsupported compression for " + name + " in embedded tzdata") | ||
} | ||
|
||
// See time.loadTzinfoFromZip for zip per-file header layout. | ||
idx = off | ||
if get4s(z[idx:]) != zheader || | ||
get2s(z[idx+8:]) != meth || | ||
get2s(z[idx+26:]) != namelen || | ||
z[idx+30:idx+30+namelen] != name { | ||
return "", errors.New("corrupt embedded tzdata") | ||
} | ||
xlen = get2s(z[idx+28:]) | ||
idx += 30 + namelen + xlen | ||
return z[idx : idx+size], nil | ||
} | ||
|
||
return "", syscall.ENOENT | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// Copyright 2020 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package tzdata | ||
|
||
import ( | ||
"reflect" | ||
"testing" | ||
"time" | ||
) | ||
|
||
var zones = []string{ | ||
"Asia/Jerusalem", | ||
"America/Los_Angeles", | ||
} | ||
|
||
func TestEmbeddedTZData(t *testing.T) { | ||
for _, zone := range zones { | ||
ref, err := time.LoadLocation(zone) | ||
if err != nil { | ||
t.Errorf("LoadLocation(%q): %v", zone, err) | ||
continue | ||
} | ||
|
||
embedded, err := loadFromEmbeddedTZData(zone) | ||
if err != nil { | ||
t.Errorf("loadFromEmbeddedTZData(%q): %v", zone, err) | ||
continue | ||
} | ||
sample, err := time.LoadLocationFromTZData(zone, []byte(embedded)) | ||
if err != nil { | ||
t.Errorf("LoadLocationFromTZData failed for %q: %v", zone, err) | ||
continue | ||
} | ||
|
||
// Compare the name and zone fields of ref and sample. | ||
// The tx field changes faster as tzdata is updated. | ||
// The cache fields are expected to differ. | ||
v1 := reflect.ValueOf(ref).Elem() | ||
v2 := reflect.ValueOf(sample).Elem() | ||
typ := v1.Type() | ||
nf := typ.NumField() | ||
found := 0 | ||
for i := 0; i < nf; i++ { | ||
ft := typ.Field(i) | ||
if ft.Name != "name" && ft.Name != "zone" { | ||
continue | ||
} | ||
found++ | ||
if !equal(t, v1.Field(i), v2.Field(i)) { | ||
t.Errorf("zone %s: system and embedded tzdata field %s differs", zone, ft.Name) | ||
} | ||
} | ||
if found != 2 { | ||
t.Errorf("test must be updated for change to time.Location struct") | ||
} | ||
} | ||
} | ||
|
||
// equal is a small version of reflect.DeepEqual that we use to | ||
// compare the values of zoneinfo unexported fields. | ||
func equal(t *testing.T, f1, f2 reflect.Value) bool { | ||
switch f1.Type().Kind() { | ||
case reflect.Slice: | ||
if f1.Len() != f2.Len() { | ||
return false | ||
} | ||
for i := 0; i < f1.Len(); i++ { | ||
if !equal(t, f1.Index(i), f2.Index(i)) { | ||
return false | ||
} | ||
} | ||
return true | ||
case reflect.Struct: | ||
nf := f1.Type().NumField() | ||
for i := 0; i < nf; i++ { | ||
if !equal(t, f1.Field(i), f2.Field(i)) { | ||
return false | ||
} | ||
} | ||
return true | ||
case reflect.String: | ||
return f1.String() == f2.String() | ||
case reflect.Bool: | ||
return f1.Bool() == f2.Bool() | ||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
return f1.Int() == f2.Int() | ||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | ||
return f1.Uint() == f2.Uint() | ||
default: | ||
t.Errorf("test internal error: unsupported kind %v", f1.Type().Kind()) | ||
return true | ||
} | ||
} |
Oops, something went wrong.