summaryrefslogtreecommitdiff
path: root/src/time/zoneinfo_read.go
diff options
context:
space:
mode:
authorFlorian Uekermann <florian@uekermann.me>2017-09-29 17:12:58 +0200
committerIan Lance Taylor <iant@golang.org>2017-10-03 14:35:32 +0000
commit630d176e7d5e42e21d176d1b9d48de0e03e7cec8 (patch)
tree5abda21a8ca7656e93496d44a6bf051f67900f7f /src/time/zoneinfo_read.go
parenta9345307a1c28315c340bbb8bbd2faa55b1b9d0a (diff)
downloadgo-git-630d176e7d5e42e21d176d1b9d48de0e03e7cec8.tar.gz
time: enable ZONEINFO tzdata file support and error reporting
Loading location data from tzdata files was only supported from default paths on android. This change enables support on all OS via the ZONEINFO environment variable and reduces the amount of android specific code significantly. Furthermore, unsuccessful calls to LoadLocation now return the first error encountered, including errors from attempting to load a location from the source specified by ZONEINFO. Errors indicating that the source or location was not found are ignored until all possible sources have been traversed. Change-Id: I45bc23b92253c9447f12f95f3ca29a7e613ed995 Reviewed-on: https://go-review.googlesource.com/67170 Reviewed-by: Ian Lance Taylor <iant@golang.org>
Diffstat (limited to 'src/time/zoneinfo_read.go')
-rw-r--r--src/time/zoneinfo_read.go71
1 files changed, 56 insertions, 15 deletions
diff --git a/src/time/zoneinfo_read.go b/src/time/zoneinfo_read.go
index eaaaf1f2b4..22658fc28a 100644
--- a/src/time/zoneinfo_read.go
+++ b/src/time/zoneinfo_read.go
@@ -220,18 +220,6 @@ func newLocationFromTzinfo(name string, Tzinfo []byte) (*Location, error) {
return l, nil
}
-// loadTzinfoFromDirOrZip returns the contents of the file with the given name
-// in dir. dir can either be an uncompressed zip file, or a directory.
-func loadTzinfoFromDirOrZip(dir, name string) ([]byte, error) {
- if len(dir) > 4 && dir[len(dir)-4:] == ".zip" {
- return loadTzinfoFromZip(dir, name)
- }
- if dir != "" {
- name = dir + "/" + name
- }
- return readFile(name)
-}
-
// 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
@@ -363,13 +351,61 @@ func loadTzinfoFromZip(zipfile, name string) ([]byte, error) {
return buf, nil
}
- return nil, errors.New("cannot find " + name + " in zip file " + zipfile)
+ return nil, syscall.ENOENT
}
// loadTzinfoFromTzdata returns the time zone information of the time zone
// with the given name, from a tzdata database file as they are typically
// found on android.
-var loadTzinfoFromTzdata func(file, name string) ([]byte, error)
+func loadTzinfoFromTzdata(file, name string) ([]byte, error) {
+ const (
+ headersize = 12 + 3*4
+ namesize = 40
+ entrysize = namesize + 3*4
+ )
+ if len(name) > namesize {
+ return nil, errors.New(name + " is longer than the maximum zone name length (40 bytes)")
+ }
+ fd, err := open(file)
+ if err != nil {
+ return nil, err
+ }
+ defer closefd(fd)
+
+ buf := make([]byte, headersize)
+ if err := preadn(fd, buf, 0); err != nil {
+ return nil, errors.New("corrupt tzdata file " + file)
+ }
+ d := data{buf, false}
+ if magic := d.read(6); string(magic) != "tzdata" {
+ return nil, errors.New("corrupt tzdata file " + file)
+ }
+ d = data{buf[12:], false}
+ indexOff, _ := d.big4()
+ dataOff, _ := d.big4()
+ indexSize := dataOff - indexOff
+ entrycount := indexSize / entrysize
+ buf = make([]byte, indexSize)
+ if err := preadn(fd, buf, int(indexOff)); err != nil {
+ return nil, errors.New("corrupt tzdata file " + file)
+ }
+ for i := 0; i < int(entrycount); i++ {
+ entry := buf[i*entrysize : (i+1)*entrysize]
+ // len(name) <= namesize is checked at function entry
+ if string(entry[:len(name)]) != name {
+ continue
+ }
+ d := data{entry[namesize:], false}
+ off, _ := d.big4()
+ size, _ := d.big4()
+ buf := make([]byte, size)
+ if err := preadn(fd, buf, int(off+dataOff)); err != nil {
+ return nil, errors.New("corrupt tzdata file " + file)
+ }
+ return buf, nil
+ }
+ return nil, syscall.ENOENT
+}
// loadTzinfo returns the time zone information of the time zone
// with the given name, from a given source. A source may be a
@@ -378,8 +414,13 @@ var loadTzinfoFromTzdata func(file, name string) ([]byte, error)
func loadTzinfo(name string, source string) ([]byte, error) {
if len(source) >= 6 && source[len(source)-6:] == "tzdata" {
return loadTzinfoFromTzdata(source, name)
+ } else if len(source) > 4 && source[len(source)-4:] == ".zip" {
+ return loadTzinfoFromZip(source, name)
+ }
+ if source != "" {
+ name = source + "/" + name
}
- return loadTzinfoFromDirOrZip(source, name)
+ return readFile(name)
}
// loadLocation returns the Location with the given name from one of