Commit 80b31c05 authored by Elias Naur's avatar Elias Naur

time: load time zones from the system tzdata file on Android

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/24494Reviewed-by: default avatarHyang-Ah Hana Kim <hyangah@gmail.com>
Reviewed-by: default avatarDavid Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 0df5ab7e
// 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]
}
}
// 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)
}
// 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)
}
}
......@@ -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.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment