diff options
Diffstat (limited to 'libgo/go/time/zoneinfo_android.go')
-rw-r--r-- | libgo/go/time/zoneinfo_android.go | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/libgo/go/time/zoneinfo_android.go b/libgo/go/time/zoneinfo_android.go new file mode 100644 index 0000000000..695a8adfaa --- /dev/null +++ b/libgo/go/time/zoneinfo_android.go @@ -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) +} |