github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/pkg/time/zoneinfo.go (about) 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 package time 6 7 import ( 8 "sync" 9 "syscall" 10 ) 11 12 // A Location maps time instants to the zone in use at that time. 13 // Typically, the Location represents the collection of time offsets 14 // in use in a geographical area, such as CEST and CET for central Europe. 15 type Location struct { 16 name string 17 zone []zone 18 tx []zoneTrans 19 20 // Most lookups will be for the current time. 21 // To avoid the binary search through tx, keep a 22 // static one-element cache that gives the correct 23 // zone for the time when the Location was created. 24 // if cacheStart <= t <= cacheEnd, 25 // lookup can return cacheZone. 26 // The units for cacheStart and cacheEnd are seconds 27 // since January 1, 1970 UTC, to match the argument 28 // to lookup. 29 cacheStart int64 30 cacheEnd int64 31 cacheZone *zone 32 } 33 34 // A zone represents a single time zone such as CEST or CET. 35 type zone struct { 36 name string // abbreviated name, "CET" 37 offset int // seconds east of UTC 38 isDST bool // is this zone Daylight Savings Time? 39 } 40 41 // A zoneTrans represents a single time zone transition. 42 type zoneTrans struct { 43 when int64 // transition time, in seconds since 1970 GMT 44 index uint8 // the index of the zone that goes into effect at that time 45 isstd, isutc bool // ignored - no idea what these mean 46 } 47 48 // UTC represents Universal Coordinated Time (UTC). 49 var UTC *Location = &utcLoc 50 51 // utcLoc is separate so that get can refer to &utcLoc 52 // and ensure that it never returns a nil *Location, 53 // even if a badly behaved client has changed UTC. 54 var utcLoc = Location{name: "UTC"} 55 56 // Local represents the system's local time zone. 57 var Local *Location = &localLoc 58 59 // localLoc is separate so that initLocal can initialize 60 // it even if a client has changed Local. 61 var localLoc Location 62 var localOnce sync.Once 63 64 func (l *Location) get() *Location { 65 if l == nil { 66 return &utcLoc 67 } 68 if l == &localLoc { 69 localOnce.Do(initLocal) 70 } 71 return l 72 } 73 74 // String returns a descriptive name for the time zone information, 75 // corresponding to the argument to LoadLocation. 76 func (l *Location) String() string { 77 return l.get().name 78 } 79 80 // FixedZone returns a Location that always uses 81 // the given zone name and offset (seconds east of UTC). 82 func FixedZone(name string, offset int) *Location { 83 l := &Location{ 84 name: name, 85 zone: []zone{{name, offset, false}}, 86 tx: []zoneTrans{{-1 << 63, 0, false, false}}, 87 cacheStart: -1 << 63, 88 cacheEnd: 1<<63 - 1, 89 } 90 l.cacheZone = &l.zone[0] 91 return l 92 } 93 94 // lookup returns information about the time zone in use at an 95 // instant in time expressed as seconds since January 1, 1970 00:00:00 UTC. 96 // 97 // The returned information gives the name of the zone (such as "CET"), 98 // the start and end times bracketing sec when that zone is in effect, 99 // the offset in seconds east of UTC (such as -5*60*60), and whether 100 // the daylight savings is being observed at that time. 101 func (l *Location) lookup(sec int64) (name string, offset int, isDST bool, start, end int64) { 102 l = l.get() 103 104 if len(l.tx) == 0 { 105 name = "UTC" 106 offset = 0 107 isDST = false 108 start = -1 << 63 109 end = 1<<63 - 1 110 return 111 } 112 113 if zone := l.cacheZone; zone != nil && l.cacheStart <= sec && sec < l.cacheEnd { 114 name = zone.name 115 offset = zone.offset 116 isDST = zone.isDST 117 start = l.cacheStart 118 end = l.cacheEnd 119 return 120 } 121 122 // Binary search for entry with largest time <= sec. 123 // Not using sort.Search to avoid dependencies. 124 tx := l.tx 125 end = 1<<63 - 1 126 lo := 0 127 hi := len(tx) 128 for hi-lo > 1 { 129 m := lo + (hi-lo)/2 130 lim := tx[m].when 131 if sec < lim { 132 end = lim 133 hi = m 134 } else { 135 lo = m 136 } 137 } 138 zone := &l.zone[tx[lo].index] 139 name = zone.name 140 offset = zone.offset 141 isDST = zone.isDST 142 start = tx[lo].when 143 // end = maintained during the search 144 return 145 } 146 147 // lookupName returns information about the time zone with 148 // the given name (such as "EST") at the given pseudo-Unix time 149 // (what the given time of day would be in UTC). 150 func (l *Location) lookupName(name string, unix int64) (offset int, isDST bool, ok bool) { 151 l = l.get() 152 153 // First try for a zone with the right name that was actually 154 // in effect at the given time. (In Sydney, Australia, both standard 155 // and daylight-savings time are abbreviated "EST". Using the 156 // offset helps us pick the right one for the given time. 157 // It's not perfect: during the backward transition we might pick 158 // either one.) 159 for i := range l.zone { 160 zone := &l.zone[i] 161 if zone.name == name { 162 nam, offset, isDST, _, _ := l.lookup(unix - int64(zone.offset)) 163 if nam == zone.name { 164 return offset, isDST, true 165 } 166 } 167 } 168 169 // Otherwise fall back to an ordinary name match. 170 for i := range l.zone { 171 zone := &l.zone[i] 172 if zone.name == name { 173 return zone.offset, zone.isDST, true 174 } 175 } 176 177 // Otherwise, give up. 178 return 179 } 180 181 // lookupOffset returns information about the time zone with 182 // the given offset (such as -5*60*60). 183 func (l *Location) lookupOffset(offset int) (name string, isDST bool, ok bool) { 184 l = l.get() 185 for i := range l.zone { 186 zone := &l.zone[i] 187 if zone.offset == offset { 188 return zone.name, zone.isDST, true 189 } 190 } 191 return 192 } 193 194 // NOTE(rsc): Eventually we will need to accept the POSIX TZ environment 195 // syntax too, but I don't feel like implementing it today. 196 197 var zoneinfo, _ = syscall.Getenv("ZONEINFO") 198 199 // LoadLocation returns the Location with the given name. 200 // 201 // If the name is "" or "UTC", LoadLocation returns UTC. 202 // If the name is "Local", LoadLocation returns Local. 203 // 204 // Otherwise, the name is taken to be a location name corresponding to a file 205 // in the IANA Time Zone database, such as "America/New_York". 206 // 207 // The time zone database needed by LoadLocation may not be 208 // present on all systems, especially non-Unix systems. 209 // LoadLocation looks in the directory or uncompressed zip file 210 // named by the ZONEINFO environment variable, if any, then looks in 211 // known installation locations on Unix systems, 212 // and finally looks in $GOROOT/lib/time/zoneinfo.zip. 213 func LoadLocation(name string) (*Location, error) { 214 if name == "" || name == "UTC" { 215 return UTC, nil 216 } 217 if name == "Local" { 218 return Local, nil 219 } 220 if zoneinfo != "" { 221 if z, err := loadZoneFile(zoneinfo, name); err == nil { 222 z.name = name 223 return z, nil 224 } 225 } 226 return loadLocation(name) 227 }