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  }