-
Notifications
You must be signed in to change notification settings - Fork 17.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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/24494 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com> Reviewed-by: David Crawshaw <crawshaw@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
- Loading branch information
Elias Naur
committed
Aug 23, 2016
1 parent
0df5ab7
commit 80b31c0
Showing
4 changed files
with
150 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters