mirror of
https://github.com/golang/go
synced 2024-11-22 02:54:39 -07:00
time: switch to using (uncompressed) zoneinfo zip file
Removal of old zoneinfo files is a separate CL due to its size. R=golang-dev, bradfitz, r CC=golang-dev https://golang.org/cl/5676100
This commit is contained in:
parent
3fb5f329b9
commit
cb5e181fe7
@ -1,4 +1,4 @@
|
||||
The zoneinfo directory contains time zone files compiled using
|
||||
The zoneinfo.zip archive contains time zone files compiled using
|
||||
the code and data maintained as part of the IANA Time Zone Database.
|
||||
The IANA asserts that the database is in the public domain.
|
||||
|
||||
@ -7,3 +7,4 @@ http://www.iana.org/time-zones
|
||||
ftp://ftp.iana.org/tz/code/tz-link.htm
|
||||
http://tools.ietf.org/html/draft-lear-iana-timezone-database-05
|
||||
|
||||
To rebuild the archive, read and run update.bash.
|
||||
|
@ -11,9 +11,10 @@ CODE=2011i
|
||||
DATA=2011n
|
||||
|
||||
set -e
|
||||
rm -rf zoneinfo work
|
||||
mkdir zoneinfo work
|
||||
rm -rf work
|
||||
mkdir work
|
||||
cd work
|
||||
mkdir zoneinfo
|
||||
curl -O http://www.iana.org/time-zones/repository/releases/tzcode$CODE.tar.gz
|
||||
curl -O http://www.iana.org/time-zones/repository/releases/tzdata$DATA.tar.gz
|
||||
tar xzf tzcode$CODE.tar.gz
|
||||
@ -23,23 +24,27 @@ tar xzf tzdata$DATA.tar.gz
|
||||
# We don't need those until 2037.
|
||||
perl -p -i -e 's/pass <= 2/pass <= 1/' zic.c
|
||||
|
||||
make CFLAGS=-DSTD_INSPIRED AWK=awk TZDIR=../zoneinfo posix_only
|
||||
make CFLAGS=-DSTD_INSPIRED AWK=awk TZDIR=zoneinfo posix_only
|
||||
|
||||
# America/Los_Angeles should not be bigger than 1100 bytes.
|
||||
# If it is, we probably failed to disable the 64-bit output, which
|
||||
# triples the size of the files.
|
||||
size=$(ls -l ../zoneinfo/America/Los_Angeles | awk '{print $5}')
|
||||
size=$(ls -l zoneinfo/America/Los_Angeles | awk '{print $5}')
|
||||
if [ $size -gt 1200 ]; then
|
||||
echo 'zone file too large; 64-bit edit failed?' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
cd ..
|
||||
hg addremove zoneinfo
|
||||
cd zoneinfo
|
||||
rm -f ../../zoneinfo.zip
|
||||
zip -0 -r ../../zoneinfo.zip *
|
||||
cd ../..
|
||||
|
||||
echo
|
||||
if [ "$1" == "-work" ]; then
|
||||
echo Left workspace behind in work/.
|
||||
else
|
||||
rm -rf work
|
||||
fi
|
||||
echo New time zone files in zoneinfo/.
|
||||
echo New time zone files in zoneinfo.zip.
|
||||
|
||||
|
BIN
lib/time/zoneinfo.zip
Normal file
BIN
lib/time/zoneinfo.zip
Normal file
Binary file not shown.
@ -6,7 +6,10 @@
|
||||
|
||||
package time
|
||||
|
||||
import "syscall"
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// for testing: whatever interrupts a sleep
|
||||
func interrupt() {
|
||||
@ -38,3 +41,35 @@ func readFile(name string) ([]byte, error) {
|
||||
}
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func open(name string) (uintptr, error) {
|
||||
fd, err := syscall.Open(name, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uintptr(fd), nil
|
||||
}
|
||||
|
||||
func closefd(fd uintptr) {
|
||||
syscall.Close(int(fd))
|
||||
}
|
||||
|
||||
func preadn(fd uintptr, buf []byte, off int) error {
|
||||
whence := 0
|
||||
if off < 0 {
|
||||
whence = 2
|
||||
}
|
||||
if _, err := syscall.Seek(int(fd), int64(off), whence); err != nil {
|
||||
return err
|
||||
}
|
||||
for len(buf) > 0 {
|
||||
m, err := syscall.Read(int(fd), buf)
|
||||
if m <= 0 {
|
||||
if err == nil {
|
||||
return errors.New("short read")
|
||||
}
|
||||
return err
|
||||
}
|
||||
buf = buf[m:]
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,10 @@
|
||||
|
||||
package time
|
||||
|
||||
import "syscall"
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// for testing: whatever interrupts a sleep
|
||||
func interrupt() {
|
||||
@ -38,3 +41,36 @@ func readFile(name string) ([]byte, error) {
|
||||
}
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func open(name string) (uintptr, error) {
|
||||
fd, err := syscall.Open(name, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uintptr(fd), nil
|
||||
}
|
||||
|
||||
func closefd(fd uintptr) {
|
||||
syscall.Close(int(fd))
|
||||
}
|
||||
|
||||
func preadn(fd uintptr, buf []byte, off int) error {
|
||||
whence := 0
|
||||
if off < 0 {
|
||||
whence = 2
|
||||
}
|
||||
if _, err := syscall.Seek(int(fd), int64(off), whence); err != nil {
|
||||
return err
|
||||
}
|
||||
for len(buf) > 0 {
|
||||
m, err := syscall.Read(int(fd), buf)
|
||||
if m <= 0 {
|
||||
if err == nil {
|
||||
return errors.New("short read")
|
||||
}
|
||||
return err
|
||||
}
|
||||
buf = buf[m:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -4,6 +4,70 @@
|
||||
|
||||
package time
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// for testing: whatever interrupts a sleep
|
||||
func interrupt() {
|
||||
}
|
||||
|
||||
// readFile reads and returns the content of the named file.
|
||||
// It is a trivial implementation of ioutil.ReadFile, reimplemented
|
||||
// here to avoid depending on io/ioutil or os.
|
||||
func readFile(name string) ([]byte, error) {
|
||||
f, err := syscall.Open(name, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.Close(f)
|
||||
var (
|
||||
buf [4096]byte
|
||||
ret []byte
|
||||
n int
|
||||
)
|
||||
for {
|
||||
n, err = syscall.Read(f, buf[:])
|
||||
if n > 0 {
|
||||
ret = append(ret, buf[:n]...)
|
||||
}
|
||||
if n == 0 || err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func open(name string) (uintptr, error) {
|
||||
fd, err := syscall.Open(name, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uintptr(fd), nil
|
||||
}
|
||||
|
||||
func closefd(fd uintptr) {
|
||||
syscall.Close(syscall.Handle(fd))
|
||||
}
|
||||
|
||||
func preadn(fd uintptr, buf []byte, off int) error {
|
||||
whence := 0
|
||||
if off < 0 {
|
||||
whence = 2
|
||||
}
|
||||
if _, err := syscall.Seek(syscall.Handle(fd), int64(off), whence); err != nil {
|
||||
return err
|
||||
}
|
||||
for len(buf) > 0 {
|
||||
m, err := syscall.Read(syscall.Handle(fd), buf)
|
||||
if m <= 0 {
|
||||
if err == nil {
|
||||
return errors.New("short read")
|
||||
}
|
||||
return err
|
||||
}
|
||||
buf = buf[m:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -183,9 +183,10 @@ var zoneinfo, _ = syscall.Getenv("ZONEINFO")
|
||||
//
|
||||
// The time zone database needed by LoadLocation may not be
|
||||
// present on all systems, especially non-Unix systems.
|
||||
// LoadLocation looks in the directory named by the ZONEINFO environment
|
||||
// variable, if any, then looks in known installation locations on Unix systems,
|
||||
// and finally looks in $GOROOT/lib/time/zoneinfo.
|
||||
// LoadLocation looks in the directory or uncompressed zip file
|
||||
// named by the ZONEINFO environment variable, if any, then looks in
|
||||
// known installation locations on Unix systems,
|
||||
// and finally looks in $GOROOT/lib/time/zoneinfo.zip.
|
||||
func LoadLocation(name string) (*Location, error) {
|
||||
if name == "" || name == "UTC" {
|
||||
return UTC, nil
|
||||
@ -194,7 +195,7 @@ func LoadLocation(name string) (*Location, error) {
|
||||
return Local, nil
|
||||
}
|
||||
if zoneinfo != "" {
|
||||
if z, err := loadZoneFile(zoneinfo + "/" + name); err == nil {
|
||||
if z, err := loadZoneFile(zoneinfo, name); err == nil {
|
||||
z.name = name
|
||||
return z, nil
|
||||
}
|
||||
|
@ -194,10 +194,148 @@ func loadZoneData(bytes []byte) (l *Location, err error) {
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func loadZoneFile(name string) (l *Location, err error) {
|
||||
func loadZoneFile(dir, name string) (l *Location, err error) {
|
||||
if len(dir) > 4 && dir[len(dir)-4:] == ".zip" {
|
||||
return loadZoneZip(dir, name)
|
||||
}
|
||||
if dir != "" {
|
||||
name = dir + "/" + name
|
||||
}
|
||||
buf, err := readFile(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return loadZoneData(buf)
|
||||
}
|
||||
|
||||
// There are 500+ zoneinfo files. Rather than distribute them all
|
||||
// individually, we ship them in an uncompressed zip file.
|
||||
// Used this way, the zip file format serves as a commonly readable
|
||||
// container for the individual small files. We choose zip over tar
|
||||
// because zip files have a contiguous table of contents, making
|
||||
// individual file lookups faster, and because the per-file overhead
|
||||
// in a zip file is considerably less than tar's 512 bytes.
|
||||
|
||||
// get4 returns the little-endian 32-bit value in b.
|
||||
func get4(b []byte) int {
|
||||
if len(b) < 4 {
|
||||
return 0
|
||||
}
|
||||
return int(b[0]) | int(b[1])<<8 | int(b[2])<<16 | int(b[3])<<24
|
||||
}
|
||||
|
||||
// get2 returns the little-endian 16-bit value in b.
|
||||
func get2(b []byte) int {
|
||||
if len(b) < 2 {
|
||||
return 0
|
||||
}
|
||||
return int(b[0]) | int(b[1])<<8
|
||||
}
|
||||
|
||||
func loadZoneZip(zipfile, name string) (l *Location, err error) {
|
||||
fd, err := open(zipfile)
|
||||
if err != nil {
|
||||
return nil, errors.New("open " + zipfile + ": " + err.Error())
|
||||
}
|
||||
defer closefd(fd)
|
||||
|
||||
const (
|
||||
zecheader = 0x06054b50
|
||||
zcheader = 0x02014b50
|
||||
ztailsize = 22
|
||||
|
||||
zheadersize = 30
|
||||
zheader = 0x04034b50
|
||||
)
|
||||
|
||||
buf := make([]byte, ztailsize)
|
||||
if err := preadn(fd, buf, -ztailsize); err != nil || get4(buf) != zecheader {
|
||||
return nil, errors.New("corrupt zip file " + zipfile)
|
||||
}
|
||||
n := get2(buf[10:])
|
||||
size := get4(buf[12:])
|
||||
off := get4(buf[16:])
|
||||
|
||||
buf = make([]byte, size)
|
||||
if err := preadn(fd, buf, off); err != nil {
|
||||
return nil, errors.New("corrupt zip file " + zipfile)
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
// zip entry layout:
|
||||
// 0 magic[4]
|
||||
// 4 madevers[1]
|
||||
// 5 madeos[1]
|
||||
// 6 extvers[1]
|
||||
// 7 extos[1]
|
||||
// 8 flags[2]
|
||||
// 10 meth[2]
|
||||
// 12 modtime[2]
|
||||
// 14 moddate[2]
|
||||
// 16 crc[4]
|
||||
// 20 csize[4]
|
||||
// 24 uncsize[4]
|
||||
// 28 namelen[2]
|
||||
// 30 xlen[2]
|
||||
// 32 fclen[2]
|
||||
// 34 disknum[2]
|
||||
// 36 iattr[2]
|
||||
// 38 eattr[4]
|
||||
// 42 off[4]
|
||||
// 46 name[namelen]
|
||||
// 46+namelen+xlen+fclen - next header
|
||||
//
|
||||
if get4(buf) != zcheader {
|
||||
break
|
||||
}
|
||||
meth := get2(buf[10:])
|
||||
size := get4(buf[24:])
|
||||
namelen := get2(buf[28:])
|
||||
xlen := get2(buf[30:])
|
||||
fclen := get2(buf[32:])
|
||||
off := get4(buf[42:])
|
||||
zname := buf[46 : 46+namelen]
|
||||
buf = buf[46+namelen+xlen+fclen:]
|
||||
if string(zname) != name {
|
||||
continue
|
||||
}
|
||||
if meth != 0 {
|
||||
return nil, errors.New("unsupported compression for " + name + " in " + zipfile)
|
||||
}
|
||||
|
||||
// zip per-file header layout:
|
||||
// 0 magic[4]
|
||||
// 4 extvers[1]
|
||||
// 5 extos[1]
|
||||
// 6 flags[2]
|
||||
// 8 meth[2]
|
||||
// 10 modtime[2]
|
||||
// 12 moddate[2]
|
||||
// 14 crc[4]
|
||||
// 18 csize[4]
|
||||
// 22 uncsize[4]
|
||||
// 26 namelen[2]
|
||||
// 28 xlen[2]
|
||||
// 30 name[namelen]
|
||||
// 30+namelen+xlen - file data
|
||||
//
|
||||
buf = make([]byte, zheadersize+namelen)
|
||||
if err := preadn(fd, buf, off); err != nil ||
|
||||
get4(buf) != zheader ||
|
||||
get2(buf[8:]) != meth ||
|
||||
get2(buf[26:]) != namelen ||
|
||||
string(buf[30:30+namelen]) != name {
|
||||
return nil, errors.New("corrupt zip file " + zipfile)
|
||||
}
|
||||
xlen = get2(buf[28:])
|
||||
|
||||
buf = make([]byte, size)
|
||||
if err := preadn(fd, buf, off+30+namelen+xlen); err != nil {
|
||||
return nil, errors.New("corrupt zip file " + zipfile)
|
||||
}
|
||||
|
||||
return loadZoneData(buf)
|
||||
}
|
||||
|
||||
return nil, errors.New("cannot find " + name + " in zip file " + zipfile)
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
func initTestingZone() {
|
||||
z, err := loadZoneFile(runtime.GOROOT() + "/lib/time/zoneinfo/" + "America/Los_Angeles")
|
||||
z, err := loadZoneFile(runtime.GOROOT()+"/lib/time/zoneinfo.zip", "America/Los_Angeles")
|
||||
if err != nil {
|
||||
panic("cannot load America/Los_Angeles for testing: " + err.Error())
|
||||
}
|
||||
@ -44,7 +44,7 @@ func initLocal() {
|
||||
tz, ok := syscall.Getenv("TZ")
|
||||
switch {
|
||||
case !ok:
|
||||
z, err := loadZoneFile("/etc/localtime")
|
||||
z, err := loadZoneFile("", "/etc/localtime")
|
||||
if err == nil {
|
||||
localLoc = *z
|
||||
localLoc.name = "Local"
|
||||
@ -63,7 +63,7 @@ func initLocal() {
|
||||
|
||||
func loadLocation(name string) (*Location, error) {
|
||||
for _, zoneDir := range zoneDirs {
|
||||
if z, err := loadZoneFile(zoneDir + name); err == nil {
|
||||
if z, err := loadZoneFile(zoneDir, name); err == nil {
|
||||
z.name = name
|
||||
return z, nil
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ package time
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"runtime"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
@ -152,7 +153,7 @@ func initLocal() {
|
||||
}
|
||||
|
||||
func loadLocation(name string) (*Location, error) {
|
||||
if z, err := loadZoneFile(runtime.GOROOT() + `\lib\time\zoneinfo\` + name); err == nil {
|
||||
if z, err := loadZoneFile(runtime.GOROOT()+`\lib\time\zoneinfo.zip`, name); err == nil {
|
||||
z.name = name
|
||||
return z, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user