]> Cypherpunks repositories - gostls13.git/commitdiff
time: enable ZONEINFO tzdata file support and error reporting
authorFlorian Uekermann <florian@uekermann.me>
Fri, 29 Sep 2017 15:12:58 +0000 (17:12 +0200)
committerIan Lance Taylor <iant@golang.org>
Tue, 3 Oct 2017 14:35:32 +0000 (14:35 +0000)
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>
src/time/zoneinfo.go
src/time/zoneinfo_android.go
src/time/zoneinfo_read.go

index 4424b4410687760b193f56d073f57d93d7bce473..4e2a207200f66d167de368be1df24a33d326ffcc 100644 (file)
@@ -272,7 +272,7 @@ var zoneinfoOnce sync.Once
 //
 // The time zone database needed by LoadLocation may not be
 // present on all systems, especially non-Unix systems.
-// LoadLocation looks in the directory or uncompressed zip file
+// LoadLocation looks in the directory, uncompressed zip file, or tzdata 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.
@@ -292,14 +292,13 @@ func LoadLocation(name string) (*Location, error) {
                env, _ := syscall.Getenv("ZONEINFO")
                zoneinfo = &env
        })
+       sources := zoneSources
        if *zoneinfo != "" {
-               if zoneData, err := loadTzinfoFromDirOrZip(*zoneinfo, name); err == nil {
-                       if z, err := newLocationFromTzinfo(name, zoneData); err == nil {
-                               return z, nil
-                       }
-               }
+               sources = make([]string, len(zoneSources)+1)
+               sources[0] = *zoneinfo
+               copy(sources[1:], zoneSources)
        }
-       return loadLocation(name, zoneSources)
+       return loadLocation(name, sources)
 }
 
 // containsDotDot reports whether s contains "..".
index 40c8ae04ea04f535f7ccbc4146629221dc389437..677b06e7e57a9bdafed43bc02bf32bae92adc5f9 100644 (file)
@@ -9,7 +9,6 @@
 package time
 
 import (
-       "errors"
        "runtime"
 )
 
@@ -23,57 +22,3 @@ func initLocal() {
        // TODO(elias.naur): getprop persist.sys.timezone
        localLoc = *UTC
 }
-
-func init() {
-       loadTzinfoFromTzdata = androidLoadTzinfoFromTzdata
-}
-
-func androidLoadTzinfoFromTzdata(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, errors.New("cannot find " + name + " in tzdata file " + file)
-}
index eaaaf1f2b4c23639da16be39e03e6587d3db0c5f..22658fc28af2e99b3839d177bb38d6cca429066d 100644 (file)
@@ -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