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