// 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" // 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. For many Locations the time offset varies // depending on whether daylight savings time is in use at the time instant. type Location struct { name string zone []zone tx []zoneTrans // The tzdata information can be followed by a string that describes // how to handle DST transitions not recorded in zoneTrans. // The format is the TZ environment variable without a colon; see // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html. // Example string, for America/Los_Angeles: PST8PDT,M3.2.0,M11.1.0 extend string // 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 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. // On Unix systems, Local consults the TZ environment // variable to find the time zone to use. No TZ means // use the system default /etc/localtime. // TZ="" means use UTC. // TZ="foo" means use file foo in the system timezone directory. 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 name argument to LoadLocation or FixedZone. func (l *Location) String() string { return l.get().name } // 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, start, end int64, isDST bool) { l = l.get() if len(l.zone) == 0 { name = "UTC" offset = 0 start = alpha end = omega isDST = false return } if zone := l.cacheZone; zone != nil && l.cacheStart <= sec && sec < l.cacheEnd { name = zone.name offset = zone.offset start = l.cacheStart end = l.cacheEnd isDST = zone.isDST return } /* if len(l.tx) == 0 || sec < l.tx[0].when { zone := &l.zone[l.lookupFirstZone()] name = zone.name offset = zone.offset start = alpha if len(l.tx) > 0 { end = l.tx[0].when } else { end = omega } isDST = zone.isDST 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 start = tx[lo].when // end = maintained during the search isDST = zone.isDST // If we're at the end of the known zone transitions, // try the extend string. if lo == len(tx)-1 && l.extend != "" { if ename, eoffset, estart, eend, eisDST, ok := tzset(l.extend, start, sec); ok { return ename, eoffset, estart, eend, eisDST } } return */ panic("todo: Location.lookup") }