Skip to content

Commit 630d176

Browse files
FlorianUekermannianlancetaylor
authored andcommitted
time: enable ZONEINFO tzdata file support and error reporting
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 <[email protected]>
1 parent a934530 commit 630d176

File tree

3 files changed

+62
-77
lines changed

3 files changed

+62
-77
lines changed

src/time/zoneinfo.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ var zoneinfoOnce sync.Once
272272
//
273273
// The time zone database needed by LoadLocation may not be
274274
// present on all systems, especially non-Unix systems.
275-
// LoadLocation looks in the directory or uncompressed zip file
275+
// LoadLocation looks in the directory, uncompressed zip file, or tzdata file
276276
// named by the ZONEINFO environment variable, if any, then looks in
277277
// known installation locations on Unix systems,
278278
// and finally looks in $GOROOT/lib/time/zoneinfo.zip.
@@ -292,14 +292,13 @@ func LoadLocation(name string) (*Location, error) {
292292
env, _ := syscall.Getenv("ZONEINFO")
293293
zoneinfo = &env
294294
})
295+
sources := zoneSources
295296
if *zoneinfo != "" {
296-
if zoneData, err := loadTzinfoFromDirOrZip(*zoneinfo, name); err == nil {
297-
if z, err := newLocationFromTzinfo(name, zoneData); err == nil {
298-
return z, nil
299-
}
300-
}
297+
sources = make([]string, len(zoneSources)+1)
298+
sources[0] = *zoneinfo
299+
copy(sources[1:], zoneSources)
301300
}
302-
return loadLocation(name, zoneSources)
301+
return loadLocation(name, sources)
303302
}
304303

305304
// containsDotDot reports whether s contains "..".

src/time/zoneinfo_android.go

Lines changed: 0 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
package time
1010

1111
import (
12-
"errors"
1312
"runtime"
1413
)
1514

@@ -23,57 +22,3 @@ func initLocal() {
2322
// TODO(elias.naur): getprop persist.sys.timezone
2423
localLoc = *UTC
2524
}
26-
27-
func init() {
28-
loadTzinfoFromTzdata = androidLoadTzinfoFromTzdata
29-
}
30-
31-
func androidLoadTzinfoFromTzdata(file, name string) ([]byte, error) {
32-
const (
33-
headersize = 12 + 3*4
34-
namesize = 40
35-
entrysize = namesize + 3*4
36-
)
37-
if len(name) > namesize {
38-
return nil, errors.New(name + " is longer than the maximum zone name length (40 bytes)")
39-
}
40-
fd, err := open(file)
41-
if err != nil {
42-
return nil, err
43-
}
44-
defer closefd(fd)
45-
46-
buf := make([]byte, headersize)
47-
if err := preadn(fd, buf, 0); err != nil {
48-
return nil, errors.New("corrupt tzdata file " + file)
49-
}
50-
d := data{buf, false}
51-
if magic := d.read(6); string(magic) != "tzdata" {
52-
return nil, errors.New("corrupt tzdata file " + file)
53-
}
54-
d = data{buf[12:], false}
55-
indexOff, _ := d.big4()
56-
dataOff, _ := d.big4()
57-
indexSize := dataOff - indexOff
58-
entrycount := indexSize / entrysize
59-
buf = make([]byte, indexSize)
60-
if err := preadn(fd, buf, int(indexOff)); err != nil {
61-
return nil, errors.New("corrupt tzdata file " + file)
62-
}
63-
for i := 0; i < int(entrycount); i++ {
64-
entry := buf[i*entrysize : (i+1)*entrysize]
65-
// len(name) <= namesize is checked at function entry
66-
if string(entry[:len(name)]) != name {
67-
continue
68-
}
69-
d := data{entry[namesize:], false}
70-
off, _ := d.big4()
71-
size, _ := d.big4()
72-
buf := make([]byte, size)
73-
if err := preadn(fd, buf, int(off+dataOff)); err != nil {
74-
return nil, errors.New("corrupt tzdata file " + file)
75-
}
76-
return buf, nil
77-
}
78-
return nil, errors.New("cannot find " + name + " in tzdata file " + file)
79-
}

src/time/zoneinfo_read.go

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -220,18 +220,6 @@ func newLocationFromTzinfo(name string, Tzinfo []byte) (*Location, error) {
220220
return l, nil
221221
}
222222

223-
// loadTzinfoFromDirOrZip returns the contents of the file with the given name
224-
// in dir. dir can either be an uncompressed zip file, or a directory.
225-
func loadTzinfoFromDirOrZip(dir, name string) ([]byte, error) {
226-
if len(dir) > 4 && dir[len(dir)-4:] == ".zip" {
227-
return loadTzinfoFromZip(dir, name)
228-
}
229-
if dir != "" {
230-
name = dir + "/" + name
231-
}
232-
return readFile(name)
233-
}
234-
235223
// There are 500+ zoneinfo files. Rather than distribute them all
236224
// individually, we ship them in an uncompressed zip file.
237225
// Used this way, the zip file format serves as a commonly readable
@@ -363,13 +351,61 @@ func loadTzinfoFromZip(zipfile, name string) ([]byte, error) {
363351
return buf, nil
364352
}
365353

366-
return nil, errors.New("cannot find " + name + " in zip file " + zipfile)
354+
return nil, syscall.ENOENT
367355
}
368356

369357
// loadTzinfoFromTzdata returns the time zone information of the time zone
370358
// with the given name, from a tzdata database file as they are typically
371359
// found on android.
372-
var loadTzinfoFromTzdata func(file, name string) ([]byte, error)
360+
func loadTzinfoFromTzdata(file, name string) ([]byte, error) {
361+
const (
362+
headersize = 12 + 3*4
363+
namesize = 40
364+
entrysize = namesize + 3*4
365+
)
366+
if len(name) > namesize {
367+
return nil, errors.New(name + " is longer than the maximum zone name length (40 bytes)")
368+
}
369+
fd, err := open(file)
370+
if err != nil {
371+
return nil, err
372+
}
373+
defer closefd(fd)
374+
375+
buf := make([]byte, headersize)
376+
if err := preadn(fd, buf, 0); err != nil {
377+
return nil, errors.New("corrupt tzdata file " + file)
378+
}
379+
d := data{buf, false}
380+
if magic := d.read(6); string(magic) != "tzdata" {
381+
return nil, errors.New("corrupt tzdata file " + file)
382+
}
383+
d = data{buf[12:], false}
384+
indexOff, _ := d.big4()
385+
dataOff, _ := d.big4()
386+
indexSize := dataOff - indexOff
387+
entrycount := indexSize / entrysize
388+
buf = make([]byte, indexSize)
389+
if err := preadn(fd, buf, int(indexOff)); err != nil {
390+
return nil, errors.New("corrupt tzdata file " + file)
391+
}
392+
for i := 0; i < int(entrycount); i++ {
393+
entry := buf[i*entrysize : (i+1)*entrysize]
394+
// len(name) <= namesize is checked at function entry
395+
if string(entry[:len(name)]) != name {
396+
continue
397+
}
398+
d := data{entry[namesize:], false}
399+
off, _ := d.big4()
400+
size, _ := d.big4()
401+
buf := make([]byte, size)
402+
if err := preadn(fd, buf, int(off+dataOff)); err != nil {
403+
return nil, errors.New("corrupt tzdata file " + file)
404+
}
405+
return buf, nil
406+
}
407+
return nil, syscall.ENOENT
408+
}
373409

374410
// loadTzinfo returns the time zone information of the time zone
375411
// 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)
378414
func loadTzinfo(name string, source string) ([]byte, error) {
379415
if len(source) >= 6 && source[len(source)-6:] == "tzdata" {
380416
return loadTzinfoFromTzdata(source, name)
417+
} else if len(source) > 4 && source[len(source)-4:] == ".zip" {
418+
return loadTzinfoFromZip(source, name)
419+
}
420+
if source != "" {
421+
name = source + "/" + name
381422
}
382-
return loadTzinfoFromDirOrZip(source, name)
423+
return readFile(name)
383424
}
384425

385426
// loadLocation returns the Location with the given name from one of

0 commit comments

Comments
 (0)