/usr/share/go-1.6/src/time/zoneinfo.go is in golang-1.6-src 1.6.1-0ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 | // Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package time
import (
"sync"
"syscall"
)
// A Location maps time instants to the zone in use at that time.
// Typically, the Location represents the collection of time offsets
// in use in a geographical area, such as CEST and CET for central Europe.
type Location struct {
name string
zone []zone
tx []zoneTrans
// Most lookups will be for the current time.
// To avoid the binary search through tx, keep a
// static one-element cache that gives the correct
// zone for the time when the Location was created.
// if cacheStart <= t < cacheEnd,
// lookup can return cacheZone.
// The units for cacheStart and cacheEnd are seconds
// since January 1, 1970 UTC, to match the argument
// to lookup.
cacheStart int64
cacheEnd int64
cacheZone *zone
}
// A zone represents a single time zone such as CEST or CET.
type zone struct {
name string // abbreviated name, "CET"
offset int // seconds east of UTC
isDST bool // is this zone Daylight Savings Time?
}
// A zoneTrans represents a single time zone transition.
type zoneTrans struct {
when int64 // transition time, in seconds since 1970 GMT
index uint8 // the index of the zone that goes into effect at that time
isstd, isutc bool // ignored - no idea what these mean
}
// alpha and omega are the beginning and end of time for zone
// transitions.
const (
alpha = -1 << 63 // math.MinInt64
omega = 1<<63 - 1 // math.MaxInt64
)
// UTC represents Universal Coordinated Time (UTC).
var UTC *Location = &utcLoc
// utcLoc is separate so that get can refer to &utcLoc
// and ensure that it never returns a nil *Location,
// even if a badly behaved client has changed UTC.
var utcLoc = Location{name: "UTC"}
// Local represents the system's local time zone.
var Local *Location = &localLoc
// localLoc is separate so that initLocal can initialize
// it even if a client has changed Local.
var localLoc Location
var localOnce sync.Once
func (l *Location) get() *Location {
if l == nil {
return &utcLoc
}
if l == &localLoc {
localOnce.Do(initLocal)
}
return l
}
// String returns a descriptive name for the time zone information,
// corresponding to the argument to LoadLocation.
func (l *Location) String() string {
return l.get().name
}
// FixedZone returns a Location that always uses
// the given zone name and offset (seconds east of UTC).
func FixedZone(name string, offset int) *Location {
l := &Location{
name: name,
zone: []zone{{name, offset, false}},
tx: []zoneTrans{{alpha, 0, false, false}},
cacheStart: alpha,
cacheEnd: omega,
}
l.cacheZone = &l.zone[0]
return l
}
// lookup returns information about the time zone in use at an
// instant in time expressed as seconds since January 1, 1970 00:00:00 UTC.
//
// The returned information gives the name of the zone (such as "CET"),
// the start and end times bracketing sec when that zone is in effect,
// the offset in seconds east of UTC (such as -5*60*60), and whether
// the daylight savings is being observed at that time.
func (l *Location) lookup(sec int64) (name string, offset int, isDST bool, start, end int64) {
l = l.get()
if len(l.zone) == 0 {
name = "UTC"
offset = 0
isDST = false
start = alpha
end = omega
return
}
if zone := l.cacheZone; zone != nil && l.cacheStart <= sec && sec < l.cacheEnd {
name = zone.name
offset = zone.offset
isDST = zone.isDST
start = l.cacheStart
end = l.cacheEnd
return
}
if len(l.tx) == 0 || sec < l.tx[0].when {
zone := &l.zone[l.lookupFirstZone()]
name = zone.name
offset = zone.offset
isDST = zone.isDST
start = alpha
if len(l.tx) > 0 {
end = l.tx[0].when
} else {
end = omega
}
return
}
// Binary search for entry with largest time <= sec.
// Not using sort.Search to avoid dependencies.
tx := l.tx
end = omega
lo := 0
hi := len(tx)
for hi-lo > 1 {
m := lo + (hi-lo)/2
lim := tx[m].when
if sec < lim {
end = lim
hi = m
} else {
lo = m
}
}
zone := &l.zone[tx[lo].index]
name = zone.name
offset = zone.offset
isDST = zone.isDST
start = tx[lo].when
// end = maintained during the search
return
}
// lookupFirstZone returns the index of the time zone to use for times
// before the first transition time, or when there are no transition
// times.
//
// The reference implementation in localtime.c from
// http://www.iana.org/time-zones/repository/releases/tzcode2013g.tar.gz
// implements the following algorithm for these cases:
// 1) If the first zone is unused by the transitions, use it.
// 2) Otherwise, if there are transition times, and the first
// transition is to a zone in daylight time, find the first
// non-daylight-time zone before and closest to the first transition
// zone.
// 3) Otherwise, use the first zone that is not daylight time, if
// there is one.
// 4) Otherwise, use the first zone.
func (l *Location) lookupFirstZone() int {
// Case 1.
if !l.firstZoneUsed() {
return 0
}
// Case 2.
if len(l.tx) > 0 && l.zone[l.tx[0].index].isDST {
for zi := int(l.tx[0].index) - 1; zi >= 0; zi-- {
if !l.zone[zi].isDST {
return zi
}
}
}
// Case 3.
for zi := range l.zone {
if !l.zone[zi].isDST {
return zi
}
}
// Case 4.
return 0
}
// firstZoneUsed returns whether the first zone is used by some
// transition.
func (l *Location) firstZoneUsed() bool {
for _, tx := range l.tx {
if tx.index == 0 {
return true
}
}
return false
}
// lookupName returns information about the time zone with
// the given name (such as "EST") at the given pseudo-Unix time
// (what the given time of day would be in UTC).
func (l *Location) lookupName(name string, unix int64) (offset int, isDST bool, ok bool) {
l = l.get()
// First try for a zone with the right name that was actually
// in effect at the given time. (In Sydney, Australia, both standard
// and daylight-savings time are abbreviated "EST". Using the
// offset helps us pick the right one for the given time.
// It's not perfect: during the backward transition we might pick
// either one.)
for i := range l.zone {
zone := &l.zone[i]
if zone.name == name {
nam, offset, isDST, _, _ := l.lookup(unix - int64(zone.offset))
if nam == zone.name {
return offset, isDST, true
}
}
}
// Otherwise fall back to an ordinary name match.
for i := range l.zone {
zone := &l.zone[i]
if zone.name == name {
return zone.offset, zone.isDST, true
}
}
// Otherwise, give up.
return
}
// NOTE(rsc): Eventually we will need to accept the POSIX TZ environment
// syntax too, but I don't feel like implementing it today.
var zoneinfo, _ = syscall.Getenv("ZONEINFO")
// LoadLocation returns the Location with the given name.
//
// If the name is "" or "UTC", LoadLocation returns UTC.
// If the name is "Local", LoadLocation returns Local.
//
// Otherwise, the name is taken to be a location name corresponding to a file
// in the IANA Time Zone database, such as "America/New_York".
//
// The time zone database needed by LoadLocation may not be
// present on all systems, especially non-Unix systems.
// LoadLocation looks in the directory or uncompressed zip file
// named by the ZONEINFO environment variable, if any, then looks in
// known installation locations on Unix systems,
// and finally looks in $GOROOT/lib/time/zoneinfo.zip.
func LoadLocation(name string) (*Location, error) {
if name == "" || name == "UTC" {
return UTC, nil
}
if name == "Local" {
return Local, nil
}
if zoneinfo != "" {
if z, err := loadZoneFile(zoneinfo, name); err == nil {
z.name = name
return z, nil
}
}
return loadLocation(name)
}
|