github.com/flyinox/gosm@v0.0.0-20171117061539-16768cb62077/src/time/zoneinfo_android.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Parse the "tzdata" packed timezone file used on Android. 6 // The format is lifted from ZoneInfoDB.java and ZoneInfo.java in 7 // java/libcore/util in the AOSP. 8 9 package time 10 11 import ( 12 "errors" 13 "runtime" 14 ) 15 16 var tzdataPaths = []string{ 17 "/system/usr/share/zoneinfo/tzdata", 18 "/data/misc/zoneinfo/current/tzdata", 19 runtime.GOROOT() + "/lib/time/zoneinfo.zip", 20 } 21 22 var origTzdataPaths = tzdataPaths 23 24 func forceZipFileForTesting(zipOnly bool) { 25 tzdataPaths = make([]string, len(origTzdataPaths)) 26 copy(tzdataPaths, origTzdataPaths) 27 if zipOnly { 28 for i := 0; i < len(tzdataPaths)-1; i++ { 29 tzdataPaths[i] = "/XXXNOEXIST" 30 } 31 } 32 } 33 34 func initTestingZone() { 35 z, err := loadLocation("America/Los_Angeles") 36 if err != nil { 37 panic("cannot load America/Los_Angeles for testing: " + err.Error()) 38 } 39 z.name = "Local" 40 localLoc = *z 41 } 42 43 func initLocal() { 44 // TODO(elias.naur): getprop persist.sys.timezone 45 localLoc = *UTC 46 } 47 48 func loadLocation(name string) (*Location, error) { 49 var firstErr error 50 for _, path := range tzdataPaths { 51 var z *Location 52 var err error 53 if len(path) > 4 && path[len(path)-4:] == ".zip" { 54 z, err = loadZoneZip(path, name) 55 } else { 56 z, err = loadTzdataFile(path, name) 57 } 58 if err == nil { 59 z.name = name 60 return z, nil 61 } else if firstErr == nil && !isNotExist(err) { 62 firstErr = err 63 } 64 } 65 if firstErr != nil { 66 return nil, firstErr 67 } 68 return nil, errors.New("unknown time zone " + name) 69 } 70 71 func loadTzdataFile(file, name string) (*Location, error) { 72 const ( 73 headersize = 12 + 3*4 74 namesize = 40 75 entrysize = namesize + 3*4 76 ) 77 if len(name) > namesize { 78 return nil, errors.New(name + " is longer than the maximum zone name length (40 bytes)") 79 } 80 fd, err := open(file) 81 if err != nil { 82 return nil, err 83 } 84 defer closefd(fd) 85 86 buf := make([]byte, headersize) 87 if err := preadn(fd, buf, 0); err != nil { 88 return nil, errors.New("corrupt tzdata file " + file) 89 } 90 d := data{buf, false} 91 if magic := d.read(6); string(magic) != "tzdata" { 92 return nil, errors.New("corrupt tzdata file " + file) 93 } 94 d = data{buf[12:], false} 95 indexOff, _ := d.big4() 96 dataOff, _ := d.big4() 97 indexSize := dataOff - indexOff 98 entrycount := indexSize / entrysize 99 buf = make([]byte, indexSize) 100 if err := preadn(fd, buf, int(indexOff)); err != nil { 101 return nil, errors.New("corrupt tzdata file " + file) 102 } 103 for i := 0; i < int(entrycount); i++ { 104 entry := buf[i*entrysize : (i+1)*entrysize] 105 // len(name) <= namesize is checked at function entry 106 if string(entry[:len(name)]) != name { 107 continue 108 } 109 d := data{entry[namesize:], false} 110 off, _ := d.big4() 111 size, _ := d.big4() 112 buf := make([]byte, size) 113 if err := preadn(fd, buf, int(off+dataOff)); err != nil { 114 return nil, errors.New("corrupt tzdata file " + file) 115 } 116 return loadZoneData(buf) 117 } 118 return nil, errors.New("cannot find " + name + " in tzdata file " + file) 119 }