github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/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 // NOTE(rsc): Eventually we will need to accept the POSIX TZ environment 182 // syntax too, but I don't feel like implementing it today. 183 184 var zoneinfo, _ = syscall.Getenv("ZONEINFO") 185 186 // LoadLocation returns the Location with the given name. 187 // 188 // If the name is "" or "UTC", LoadLocation returns UTC. 189 // If the name is "Local", LoadLocation returns Local. 190 // 191 // Otherwise, the name is taken to be a location name corresponding to a file 192 // in the IANA Time Zone database, such as "America/New_York". 193 // 194 // The time zone database needed by LoadLocation may not be 195 // present on all systems, especially non-Unix systems. 196 // LoadLocation looks in the directory or uncompressed zip file 197 // named by the ZONEINFO environment variable, if any, then looks in 198 // known installation locations on Unix systems, 199 // and finally looks in $GOROOT/lib/time/zoneinfo.zip. 200 func LoadLocation(name string) (*Location, error) { 201 if name == "" || name == "UTC" { 202 return UTC, nil 203 } 204 if name == "Local" { 205 return Local, nil 206 } 207 if zoneinfo != "" { 208 if z, err := loadZoneFile(zoneinfo, name); err == nil { 209 z.name = name 210 return z, nil 211 } 212 } 213 return loadLocation(name) 214 }