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 (
	
	
	
)
go:generate env ZONEINFO=$GOROOT/lib/time/zoneinfo.zip go run genzabbrs.go -output zoneinfo_abbrs_windows.go
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
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.
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).
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.
localLoc is separate so that initLocal can initialize it even if a client has changed Local.
var localLoc Location
var localOnce sync.Once

func ( *Location) () *Location {
	if  == nil {
		return &utcLoc
	}
	if  == &localLoc {
		localOnce.Do(initLocal)
	}
	return 
}
String returns a descriptive name for the time zone information, corresponding to the name argument to LoadLocation or FixedZone.
func ( *Location) () string {
	return .get().name
}
FixedZone returns a Location that always uses the given zone name and offset (seconds east of UTC).
func ( string,  int) *Location {
	 := &Location{
		name:       ,
		zone:       []zone{{, , false}},
		tx:         []zoneTrans{{alpha, 0, false, false}},
		cacheStart: alpha,
		cacheEnd:   omega,
	}
	.cacheZone = &.zone[0]
	return 
}
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 ( *Location) ( int64) ( string,  int, ,  int64) {
	 = .get()

	if len(.zone) == 0 {
		 = "UTC"
		 = 0
		 = alpha
		 = omega
		return
	}

	if  := .cacheZone;  != nil && .cacheStart <=  &&  < .cacheEnd {
		 = .name
		 = .offset
		 = .cacheStart
		 = .cacheEnd
		return
	}

	if len(.tx) == 0 ||  < .tx[0].when {
		 := &.zone[.lookupFirstZone()]
		 = .name
		 = .offset
		 = alpha
		if len(.tx) > 0 {
			 = .tx[0].when
		} else {
			 = omega
		}
		return
	}
Binary search for entry with largest time <= sec. Not using sort.Search to avoid dependencies.
	 := .tx
	 = omega
	 := 0
	 := len()
	for - > 1 {
		 :=  + (-)/2
		 := [].when
		if  <  {
			 = 
			 = 
		} else {
			 = 
		}
	}
	 := &.zone[[].index]
	 = .name
	 = .offset
end = maintained during the search
If we're at the end of the known zone transitions, try the extend string.
	if  == len()-1 && .extend != "" {
		if , , , ,  := tzset(.extend, , );  {
			return , , , 
		}
	}

	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 https://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.
Case 1.
	if !.firstZoneUsed() {
		return 0
	}
Case 2.
	if len(.tx) > 0 && .zone[.tx[0].index].isDST {
		for  := int(.tx[0].index) - 1;  >= 0; -- {
			if !.zone[].isDST {
				return 
			}
		}
	}
Case 3.
	for  := range .zone {
		if !.zone[].isDST {
			return 
		}
	}
Case 4.
	return 0
}
firstZoneUsed reports whether the first zone is used by some transition.
func ( *Location) () bool {
	for ,  := range .tx {
		if .index == 0 {
			return true
		}
	}
	return false
}
tzset takes a timezone string like the one found in the TZ environment variable, the end of the last time zone transition expressed as seconds since January 1, 1970 00:00:00 UTC, and a time expressed the same way. We call this a tzset string since in C the function tzset reads TZ. The return values are as for lookup, plus ok which reports whether the parse succeeded.
func ( string, ,  int64) ( string,  int, ,  int64,  bool) {
	var (
		,      string
		,  int
	)

	, ,  = tzsetName()
	if  {
		, ,  = tzsetOffset()
	}
	if ! {
		return "", 0, 0, 0, false
	}
The numbers in the tzset string are added to local time to get UTC, but our offsets are added to UTC to get local time, so we negate the number we see here.
	 = -

No daylight savings time.
		return , , , omega, true
	}

	, ,  = tzsetName()
	if  {
		if len() == 0 || [0] == ',' {
			 =  + secondsPerHour
		} else {
			, ,  = tzsetOffset()
			 = - // as with stdOffset, above
		}
	}
	if ! {
		return "", 0, 0, 0, false
	}

Default DST rules per tzcode.
		 = ",M3.2.0,M11.1.0"
The TZ definition does not mention ';' here but tzcode accepts it.
	if [0] != ',' && [0] != ';' {
		return "", 0, 0, 0, false
	}
	 = [1:]

	var ,  rule
	, ,  = tzsetRule()
	if ! || len() == 0 || [0] != ',' {
		return "", 0, 0, 0, false
	}
	 = [1:]
	, ,  = tzsetRule()
	if ! || len() > 0 {
		return "", 0, 0, 0, false
	}

	, , ,  := absDate(uint64(+unixToInternal+internalToAbsolute), false)

	 := int64(*secondsPerDay) + %secondsPerDay
Compute start of year in seconds since Unix epoch.
	 := daysSinceEpoch()
	 := int64( * secondsPerDay)
	 += absoluteToInternal + internalToUnix

	 := int64(tzruleTime(, , ))
	 := int64(tzruleTime(, , ))
	if  <  {
		,  = , 
		,  = , 
		,  = , 
	}
The start and end values that we return are accurate close to a daylight savings transition, but are otherwise just the start and end of the year. That suffices for the only caller that cares, which is Date.
	if  <  {
		return , , ,  + , true
	} else if  >=  {
		return , ,  + ,  + 365*secondsPerDay, true
	} else {
		return , ,  + ,  + , true
	}
}
tzsetName returns the timezone name at the start of the tzset string s, and the remainder of s, and reports whether the parsing is OK.
func ( string) (string, string, bool) {
	if len() == 0 {
		return "", "", false
	}
	if [0] != '<' {
		for ,  := range  {
			switch  {
			case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ',', '-', '+':
				if  < 3 {
					return "", "", false
				}
				return [:], [:], true
			}
		}
		if len() < 3 {
			return "", "", false
		}
		return , "", true
	} else {
		for ,  := range  {
			if  == '>' {
				return [1:], [+1:], true
			}
		}
		return "", "", false
	}
}
tzsetOffset returns the timezone offset at the start of the tzset string s, and the remainder of s, and reports whether the parsing is OK. The timezone offset is returned as a number of seconds.
func ( string) ( int,  string,  bool) {
	if len() == 0 {
		return 0, "", false
	}
	 := false
	if [0] == '+' {
		 = [1:]
	} else if [0] == '-' {
		 = [1:]
		 = true
	}
The tzdata code permits values up to 24 * 7 here, although POSIX does not.
	var  int
	, ,  = tzsetNum(, 0, 24*7)
	if ! {
		return 0, "", false
	}
	 :=  * secondsPerHour
	if len() == 0 || [0] != ':' {
		if  {
			 = -
		}
		return , , true
	}

	var  int
	, ,  = tzsetNum([1:], 0, 59)
	if ! {
		return 0, "", false
	}
	 +=  * secondsPerMinute
	if len() == 0 || [0] != ':' {
		if  {
			 = -
		}
		return , , true
	}

	var  int
	, ,  = tzsetNum([1:], 0, 59)
	if ! {
		return 0, "", false
	}
	 += 

	if  {
		 = -
	}
	return , , true
}
ruleKind is the kinds of rules that can be seen in a tzset string.
rule is a rule read from a tzset string.
type rule struct {
	kind ruleKind
	day  int
	week int
	mon  int
	time int // transition time
}
tzsetRule parses a rule from a tzset string. It returns the rule, and the remainder of the string, and reports success.
func ( string) (rule, string, bool) {
	var  rule
	if len() == 0 {
		return rule{}, "", false
	}
	 := false
	if [0] == 'J' {
		var  int
		, ,  = tzsetNum([1:], 1, 365)
		if ! {
			return rule{}, "", false
		}
		.kind = ruleJulian
		.day = 
	} else if [0] == 'M' {
		var  int
		, ,  = tzsetNum([1:], 1, 12)
		if ! || len() == 0 || [0] != '.' {
			return rule{}, "", false

		}
		var  int
		, ,  = tzsetNum([1:], 1, 5)
		if ! || len() == 0 || [0] != '.' {
			return rule{}, "", false
		}
		var  int
		, ,  = tzsetNum([1:], 0, 6)
		if ! {
			return rule{}, "", false
		}
		.kind = ruleMonthWeekDay
		.day = 
		.week = 
		.mon = 
	} else {
		var  int
		, ,  = tzsetNum(, 0, 365)
		if ! {
			return rule{}, "", false
		}
		.kind = ruleDOY
		.day = 
	}

	if len() == 0 || [0] != '/' {
		.time = 2 * secondsPerHour // 2am is the default
		return , , true
	}

	, ,  := tzsetOffset([1:])
	if ! {
		return rule{}, "", false
	}
	.time = 

	return , , true
}
tzsetNum parses a number from a tzset string. It returns the number, and the remainder of the string, and reports success. The number must be between min and max.
func ( string, ,  int) ( int,  string,  bool) {
	if len() == 0 {
		return 0, "", false
	}
	 = 0
	for ,  := range  {
		if  < '0' ||  > '9' {
			if  == 0 ||  <  {
				return 0, "", false
			}
			return , [:], true
		}
		 *= 10
		 += int() - '0'
		if  >  {
			return 0, "", false
		}
	}
	if  <  {
		return 0, "", false
	}
	return , "", true
}
tzruleTime takes a year, a rule, and a timezone offset, and returns the number of seconds since the start of the year that the rule takes effect.
func ( int,  rule,  int) int {
	var  int
	switch .kind {
	case ruleJulian:
		 = (.day - 1) * secondsPerDay
		if isLeap() && .day >= 60 {
			 += secondsPerDay
		}
	case ruleDOY:
		 = .day * secondsPerDay
Zeller's Congruence.
		 := (.mon+9)%12 + 1
		 := 
		if .mon <= 2 {
			--
		}
		 :=  / 100
		 :=  % 100
		 := ((26*-2)/10 + 1 +  + /4 + /4 - 2*) % 7
		if  < 0 {
			 += 7
Now dow is the day-of-week of the first day of r.mon. Get the day-of-month of the first "dow" day.
		 := .day - 
		if  < 0 {
			 += 7
		}
		for  := 1;  < .week; ++ {
			if +7 >= daysIn(Month(.mon), ) {
				break
			}
			 += 7
		}
		 += int(daysBefore[.mon-1])
		if isLeap() && .mon > 2 {
			++
		}
		 =  * secondsPerDay
	}

	return  + .time - 
}
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 ( *Location) ( string,  int64) ( int,  bool) {
	 = .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  := range .zone {
		 := &.zone[]
		if .name ==  {
			, , ,  := .lookup( - int64(.offset))
			if  == .name {
				return , true
			}
		}
	}
Otherwise fall back to an ordinary name match.
	for  := range .zone {
		 := &.zone[]
		if .name ==  {
			return .offset, 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 errLocation = errors.New("time: invalid location name")

var zoneinfo *string
var zoneinfoOnce sync.Once
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 ( string) (*Location, error) {
	if  == "" ||  == "UTC" {
		return UTC, nil
	}
	if  == "Local" {
		return Local, nil
	}
No valid IANA Time Zone name contains a single dot, much less dot dot. Likewise, none begin with a slash.
		return nil, errLocation
	}
	zoneinfoOnce.Do(func() {
		,  := syscall.Getenv("ZONEINFO")
		zoneinfo = &
	})
	var  error
	if *zoneinfo != "" {
		if ,  := loadTzinfoFromDirOrZip(*zoneinfo, );  == nil {
			if ,  := LoadLocationFromTZData(, );  == nil {
				return , nil
			}
			 = 
		} else if  != syscall.ENOENT {
			 = 
		}
	}
	if ,  := loadLocation(, zoneSources);  == nil {
		return , nil
	} else if  == nil {
		 = 
	}
	return nil, 
}
containsDotDot reports whether s contains "..".
func ( string) bool {
	if len() < 2 {
		return false
	}
	for  := 0;  < len()-1; ++ {
		if [] == '.' && [+1] == '.' {
			return true
		}
	}
	return false