Source file src/pkg/time/zoneinfo.go
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 for len(tx) > 1 { 127 m := len(tx) / 2 128 lim := tx[m].when 129 if sec < lim { 130 end = lim 131 tx = tx[0:m] 132 } else { 133 tx = tx[m:] 134 } 135 } 136 zone := &l.zone[tx[0].index] 137 name = zone.name 138 offset = zone.offset 139 isDST = zone.isDST 140 start = tx[0].when 141 // end = maintained during the search 142 return 143 } 144 145 // lookupName returns information about the time zone with 146 // the given name (such as "EST"). 147 func (l *Location) lookupName(name string) (offset int, isDST bool, ok bool) { 148 l = l.get() 149 for i := range l.zone { 150 zone := &l.zone[i] 151 if zone.name == name { 152 return zone.offset, zone.isDST, true 153 } 154 } 155 return 156 } 157 158 // lookupOffset returns information about the time zone with 159 // the given offset (such as -5*60*60). 160 func (l *Location) lookupOffset(offset int) (name string, isDST bool, ok bool) { 161 l = l.get() 162 for i := range l.zone { 163 zone := &l.zone[i] 164 if zone.offset == offset { 165 return zone.name, zone.isDST, true 166 } 167 } 168 return 169 } 170 171 // NOTE(rsc): Eventually we will need to accept the POSIX TZ environment 172 // syntax too, but I don't feel like implementing it today. 173 174 var zoneinfo, _ = syscall.Getenv("ZONEINFO") 175 176 // LoadLocation returns the Location with the given name. 177 // 178 // If the name is "" or "UTC", LoadLocation returns UTC. 179 // If the name is "Local", LoadLocation returns Local. 180 // 181 // Otherwise, the name is taken to be a location name corresponding to a file 182 // in the IANA Time Zone database, such as "America/New_York". 183 // 184 // The time zone database needed by LoadLocation may not be 185 // present on all systems, especially non-Unix systems. 186 // LoadLocation looks in the directory or uncompressed zip file 187 // named by the ZONEINFO environment variable, if any, then looks in 188 // known installation locations on Unix systems, 189 // and finally looks in $GOROOT/lib/time/zoneinfo.zip. 190 func LoadLocation(name string) (*Location, error) { 191 if name == "" || name == "UTC" { 192 return UTC, nil 193 } 194 if name == "Local" { 195 return Local, nil 196 } 197 if zoneinfo != "" { 198 if z, err := loadZoneFile(zoneinfo, name); err == nil { 199 z.name = name 200 return z, nil 201 } 202 } 203 return loadLocation(name) 204 }