Run Format

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		lo := 0
   127		hi := len(tx)
   128		for hi-lo > 1 {
   129			m := lo + (hi-lo)/2
   130			lim := tx[m].when
   131			if sec < lim {
   132				end = lim
   133				hi = m
   134			} else {
   135				lo = m
   136			}
   137		}
   138		zone := &l.zone[tx[lo].index]
   139		name = zone.name
   140		offset = zone.offset
   141		isDST = zone.isDST
   142		start = tx[lo].when
   143		// end = maintained during the search
   144		return
   145	}
   146	
   147	// lookupName returns information about the time zone with
   148	// the given name (such as "EST") at the given pseudo-Unix time
   149	// (what the given time of day would be in UTC).
   150	func (l *Location) lookupName(name string, unix int64) (offset int, isDST bool, ok bool) {
   151		l = l.get()
   152	
   153		// First try for a zone with the right name that was actually
   154		// in effect at the given time. (In Sydney, Australia, both standard
   155		// and daylight-savings time are abbreviated "EST". Using the
   156		// offset helps us pick the right one for the given time.
   157		// It's not perfect: during the backward transition we might pick
   158		// either one.)
   159		for i := range l.zone {
   160			zone := &l.zone[i]
   161			if zone.name == name {
   162				nam, offset, isDST, _, _ := l.lookup(unix - int64(zone.offset))
   163				if nam == zone.name {
   164					return offset, isDST, true
   165				}
   166			}
   167		}
   168	
   169		// Otherwise fall back to an ordinary name match.
   170		for i := range l.zone {
   171			zone := &l.zone[i]
   172			if zone.name == name {
   173				return zone.offset, zone.isDST, true
   174			}
   175		}
   176	
   177		// Otherwise, give up.
   178		return
   179	}
   180	
   181	// NOTE(rsc): Eventually we will need to accept the POSIX TZ environment
   182	// syntax too, but I don't feel like implementing it today.
   183	
   184	var zoneinfo, _ = syscall.Getenv("ZONEINFO")
   185	
   186	// LoadLocation returns the Location with the given name.
   187	//
   188	// If the name is "" or "UTC", LoadLocation returns UTC.
   189	// If the name is "Local", LoadLocation returns Local.
   190	//
   191	// Otherwise, the name is taken to be a location name corresponding to a file
   192	// in the IANA Time Zone database, such as "America/New_York".
   193	//
   194	// The time zone database needed by LoadLocation may not be
   195	// present on all systems, especially non-Unix systems.
   196	// LoadLocation looks in the directory or uncompressed zip file
   197	// named by the ZONEINFO environment variable, if any, then looks in
   198	// known installation locations on Unix systems,
   199	// and finally looks in $GOROOT/lib/time/zoneinfo.zip.
   200	func LoadLocation(name string) (*Location, error) {
   201		if name == "" || name == "UTC" {
   202			return UTC, nil
   203		}
   204		if name == "Local" {
   205			return Local, nil
   206		}
   207		if zoneinfo != "" {
   208			if z, err := loadZoneFile(zoneinfo, name); err == nil {
   209				z.name = name
   210				return z, nil
   211			}
   212		}
   213		return loadLocation(name)
   214	}

View as plain text