Home | History | Annotate | Download | only in time
      1 // Copyright 2011 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 Plan 9 timezone(2) files.
      6 
      7 package time
      8 
      9 import (
     10 	"runtime"
     11 	"syscall"
     12 )
     13 
     14 func isSpace(r rune) bool {
     15 	return r == ' ' || r == '\t' || r == '\n'
     16 }
     17 
     18 // Copied from strings to avoid a dependency.
     19 func fields(s string) []string {
     20 	// First count the fields.
     21 	n := 0
     22 	inField := false
     23 	for _, rune := range s {
     24 		wasInField := inField
     25 		inField = !isSpace(rune)
     26 		if inField && !wasInField {
     27 			n++
     28 		}
     29 	}
     30 
     31 	// Now create them.
     32 	a := make([]string, n)
     33 	na := 0
     34 	fieldStart := -1 // Set to -1 when looking for start of field.
     35 	for i, rune := range s {
     36 		if isSpace(rune) {
     37 			if fieldStart >= 0 {
     38 				a[na] = s[fieldStart:i]
     39 				na++
     40 				fieldStart = -1
     41 			}
     42 		} else if fieldStart == -1 {
     43 			fieldStart = i
     44 		}
     45 	}
     46 	if fieldStart >= 0 { // Last field might end at EOF.
     47 		a[na] = s[fieldStart:]
     48 	}
     49 	return a
     50 }
     51 
     52 func loadZoneDataPlan9(s string) (l *Location, err error) {
     53 	f := fields(s)
     54 	if len(f) < 4 {
     55 		if len(f) == 2 && f[0] == "GMT" {
     56 			return UTC, nil
     57 		}
     58 		return nil, badData
     59 	}
     60 
     61 	var zones [2]zone
     62 
     63 	// standard timezone offset
     64 	o, err := atoi(f[1])
     65 	if err != nil {
     66 		return nil, badData
     67 	}
     68 	zones[0] = zone{name: f[0], offset: o, isDST: false}
     69 
     70 	// alternate timezone offset
     71 	o, err = atoi(f[3])
     72 	if err != nil {
     73 		return nil, badData
     74 	}
     75 	zones[1] = zone{name: f[2], offset: o, isDST: true}
     76 
     77 	// transition time pairs
     78 	var tx []zoneTrans
     79 	f = f[4:]
     80 	for i := 0; i < len(f); i++ {
     81 		zi := 0
     82 		if i%2 == 0 {
     83 			zi = 1
     84 		}
     85 		t, err := atoi(f[i])
     86 		if err != nil {
     87 			return nil, badData
     88 		}
     89 		t -= zones[0].offset
     90 		tx = append(tx, zoneTrans{when: int64(t), index: uint8(zi)})
     91 	}
     92 
     93 	// Committed to succeed.
     94 	l = &Location{zone: zones[:], tx: tx}
     95 
     96 	// Fill in the cache with information about right now,
     97 	// since that will be the most common lookup.
     98 	sec, _ := now()
     99 	for i := range tx {
    100 		if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) {
    101 			l.cacheStart = tx[i].when
    102 			l.cacheEnd = omega
    103 			if i+1 < len(tx) {
    104 				l.cacheEnd = tx[i+1].when
    105 			}
    106 			l.cacheZone = &l.zone[tx[i].index]
    107 		}
    108 	}
    109 
    110 	return l, nil
    111 }
    112 
    113 func loadZoneFilePlan9(name string) (*Location, error) {
    114 	b, err := readFile(name)
    115 	if err != nil {
    116 		return nil, err
    117 	}
    118 	return loadZoneDataPlan9(string(b))
    119 }
    120 
    121 func initTestingZone() {
    122 	z, err := loadLocation("America/Los_Angeles")
    123 	if err != nil {
    124 		panic("cannot load America/Los_Angeles for testing: " + err.Error())
    125 	}
    126 	z.name = "Local"
    127 	localLoc = *z
    128 }
    129 
    130 func initLocal() {
    131 	t, ok := syscall.Getenv("timezone")
    132 	if ok {
    133 		if z, err := loadZoneDataPlan9(t); err == nil {
    134 			localLoc = *z
    135 			return
    136 		}
    137 	} else {
    138 		if z, err := loadZoneFilePlan9("/adm/timezone/local"); err == nil {
    139 			localLoc = *z
    140 			localLoc.name = "Local"
    141 			return
    142 		}
    143 	}
    144 
    145 	// Fall back to UTC.
    146 	localLoc.name = "UTC"
    147 }
    148 
    149 func loadLocation(name string) (*Location, error) {
    150 	z, err := loadZoneFile(runtime.GOROOT()+"/lib/time/zoneinfo.zip", name)
    151 	if err != nil {
    152 		return nil, err
    153 	}
    154 	z.name = name
    155 	return z, nil
    156 }
    157 
    158 func forceZipFileForTesting(zipOnly bool) {
    159 	// We only use the zip file anyway.
    160 }
    161