|  | // 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" | 
|  | "syscall" | 
|  | ) | 
|  |  | 
|  | var zoneSources = []string{ | 
|  | "/system/usr/share/zoneinfo/tzdata", | 
|  | "/data/misc/zoneinfo/current/tzdata", | 
|  | runtime.GOROOT() + "/lib/time/zoneinfo.zip", | 
|  | } | 
|  |  | 
|  | func initLocal() { | 
|  | // TODO(elias.naur): getprop persist.sys.timezone | 
|  | localLoc = *UTC | 
|  | } | 
|  |  | 
|  | func init() { | 
|  | loadTzinfoFromTzdata = androidLoadTzinfoFromTzdata | 
|  | } | 
|  |  | 
|  | func androidLoadTzinfoFromTzdata(file, name string) ([]byte, 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 := dataIO{buf, false} | 
|  | if magic := d.read(6); string(magic) != "tzdata" { | 
|  | return nil, errors.New("corrupt tzdata file " + file) | 
|  | } | 
|  | d = dataIO{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 := dataIO{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 buf, nil | 
|  | } | 
|  | return nil, syscall.ENOENT | 
|  | } |