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