]> Cypherpunks repositories - gostls13.git/commitdiff
time: load time zones from the system tzdata file on Android
authorElias Naur <elias.naur@gmail.com>
Mon, 27 Jun 2016 19:38:04 +0000 (21:38 +0200)
committerElias Naur <elias.naur@gmail.com>
Tue, 23 Aug 2016 06:15:35 +0000 (06:15 +0000)
Android timezones are in a packed format, different from the separate
files of a regular Unix system. This CL contain the necessary code to
parse the packed tzdata file and extract time zones from it. It also
adds a basic test to ensure the new parser works.

Fixes #13581

Change-Id: Idebe73726c3d4c2de89dd6ae1d7d19f975207500
Reviewed-on: https://go-review.googlesource.com/24494
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
Reviewed-by: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/time/export_android_test.go [new file with mode: 0644]
src/time/zoneinfo_android.go [new file with mode: 0644]
src/time/zoneinfo_android_test.go [new file with mode: 0644]
src/time/zoneinfo_unix.go

diff --git a/src/time/export_android_test.go b/src/time/export_android_test.go
new file mode 100644 (file)
index 0000000..fa6a058
--- /dev/null
@@ -0,0 +1,12 @@
+// Copyright 2016 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 time
+
+func ForceAndroidTzdataForTest(tzdata bool) {
+       tzdataPaths = origTzdataPaths
+       if tzdata {
+               tzdataPaths = tzdataPaths[:1]
+       }
+}
diff --git a/src/time/zoneinfo_android.go b/src/time/zoneinfo_android.go
new file mode 100644 (file)
index 0000000..695a8ad
--- /dev/null
@@ -0,0 +1,119 @@
+// Copyright 2016 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.
+
+// Parse the "tzdata" packed timezone file used on Android.
+// The format is lifted from ZoneInfoDB.java and ZoneInfo.java in
+// java/libcore/util in the AOSP.
+
+package time
+
+import (
+       "errors"
+       "runtime"
+)
+
+var tzdataPaths = []string{
+       "/system/usr/share/zoneinfo/tzdata",
+       "/data/misc/zoneinfo/current/tzdata",
+       runtime.GOROOT() + "/lib/time/zoneinfo.zip",
+}
+
+var origTzdataPaths = tzdataPaths
+
+func forceZipFileForTesting(zipOnly bool) {
+       tzdataPaths = make([]string, len(origTzdataPaths))
+       copy(tzdataPaths, origTzdataPaths)
+       if zipOnly {
+               for i := 0; i < len(tzdataPaths)-1; i++ {
+                       tzdataPaths[i] = "/XXXNOEXIST"
+               }
+       }
+}
+
+func initTestingZone() {
+       z, err := loadLocation("America/Los_Angeles")
+       if err != nil {
+               panic("cannot load America/Los_Angeles for testing: " + err.Error())
+       }
+       z.name = "Local"
+       localLoc = *z
+}
+
+func initLocal() {
+       // TODO(elias.naur): getprop persist.sys.timezone
+       localLoc = *UTC
+}
+
+func loadLocation(name string) (*Location, error) {
+       var firstErr error
+       for _, path := range tzdataPaths {
+               var z *Location
+               var err error
+               if len(path) > 4 && path[len(path)-4:] == ".zip" {
+                       z, err = loadZoneZip(path, name)
+               } else {
+                       z, err = loadTzdataFile(path, name)
+               }
+               if err == nil {
+                       z.name = name
+                       return z, nil
+               } else if firstErr == nil && !isNotExist(err) {
+                       firstErr = err
+               }
+       }
+       if firstErr != nil {
+               return nil, firstErr
+       }
+       return nil, errors.New("unknown time zone " + name)
+}
+
+func loadTzdataFile(file, name string) (*Location, 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 loadZoneData(buf)
+       }
+       return nil, errors.New("cannot find " + name + " in tzdata file " + file)
+}
diff --git a/src/time/zoneinfo_android_test.go b/src/time/zoneinfo_android_test.go
new file mode 100644 (file)
index 0000000..ba065d1
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2016 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 time_test
+
+import (
+       "testing"
+       . "time"
+)
+
+func TestAndroidTzdata(t *testing.T) {
+       ForceAndroidTzdataForTest(true)
+       defer ForceAndroidTzdataForTest(false)
+       if _, err := LoadLocation("America/Los_Angeles"); err != nil {
+               t.Error(err)
+       }
+}
index ed9502da57d9687122c985cfa644503c649a5f07..bbf263a16fbe3296137d007e337eb007b07db4b2 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin,386 darwin,amd64 dragonfly freebsd linux nacl netbsd openbsd solaris
+// +build darwin,386 darwin,amd64 dragonfly freebsd linux,!android nacl netbsd openbsd solaris
 
 // Parse "zoneinfo" time zone file.
 // This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others.