summaryrefslogtreecommitdiff
path: root/src/pkg/time/zoneinfo.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/time/zoneinfo.go')
-rw-r--r--src/pkg/time/zoneinfo.go204
1 files changed, 204 insertions, 0 deletions
diff --git a/src/pkg/time/zoneinfo.go b/src/pkg/time/zoneinfo.go
new file mode 100644
index 000000000..3c5774404
--- /dev/null
+++ b/src/pkg/time/zoneinfo.go
@@ -0,0 +1,204 @@
+// 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
+}
+
+// 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{{-1 << 63, 0, false, false}},
+ cacheStart: -1 << 63,
+ cacheEnd: 1<<63 - 1,
+ }
+ 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.tx) == 0 {
+ name = "UTC"
+ offset = 0
+ isDST = false
+ start = -1 << 63
+ end = 1<<63 - 1
+ 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
+ }
+
+ // Binary search for entry with largest time <= sec.
+ // Not using sort.Search to avoid dependencies.
+ tx := l.tx
+ end = 1<<63 - 1
+ for len(tx) > 1 {
+ m := len(tx) / 2
+ lim := tx[m].when
+ if sec < lim {
+ end = lim
+ tx = tx[0:m]
+ } else {
+ tx = tx[m:]
+ }
+ }
+ zone := &l.zone[tx[0].index]
+ name = zone.name
+ offset = zone.offset
+ isDST = zone.isDST
+ start = tx[0].when
+ // end = maintained during the search
+ return
+}
+
+// lookupName returns information about the time zone with
+// the given name (such as "EST").
+func (l *Location) lookupName(name string) (offset int, isDST bool, ok bool) {
+ l = l.get()
+ for i := range l.zone {
+ zone := &l.zone[i]
+ if zone.name == name {
+ return zone.offset, zone.isDST, true
+ }
+ }
+ return
+}
+
+// lookupOffset returns information about the time zone with
+// the given offset (such as -5*60*60).
+func (l *Location) lookupOffset(offset int) (name string, isDST bool, ok bool) {
+ l = l.get()
+ for i := range l.zone {
+ zone := &l.zone[i]
+ if zone.offset == offset {
+ return zone.name, zone.isDST, true
+ }
+ }
+ 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)
+}