175 lines
4.8 KiB
Go
175 lines
4.8 KiB
Go
// 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")
|
|
}
|