// 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.
//
// Location is used to provide a time zone in a printed Time value and for
// calculations involving intervals that may cross daylight savings time
// boundaries.
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 ( *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
}

var unnamedFixedZones []*Location
var unnamedFixedZonesOnce sync.Once

// FixedZone returns a [Location] that always uses
// the given zone name and offset (seconds east of UTC).
func ( string,  int) *Location {
	// Most calls to FixedZone have an unnamed zone with an offset by the hour.
	// Optimize for that case by returning the same *Location for a given hour.
	const  = 12
	const  = 14
	 :=  / 60 / 60
	if  == "" && - <=  &&  <= + && *60*60 ==  {
		unnamedFixedZonesOnce.Do(func() {
			unnamedFixedZones = make([]*Location, +1+)
			for  := -;  <= +; ++ {
				unnamedFixedZones[+] = fixedZone("", *60*60)
			}
		})
		return unnamedFixedZones[+]
	}
	return fixedZone(, )
}

func fixedZone( 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,  bool) {
	 = .get()

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

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

	if len(.tx) == 0 ||  < .tx[0].when {
		 := &.zone[.lookupFirstZone()]
		 = .name
		 = .offset
		 = alpha
		if len(.tx) > 0 {
			 = .tx[0].when
		} else {
			 = omega
		}
		 = .isDST
		return
	}

	// Binary search for entry with largest time <= sec.
	// Not using sort.Search to avoid dependencies.
	 := .tx
	 = omega
	 := 0
	 := len()
	for - > 1 {
		 := int(uint(+) >> 1)
		 := [].when
		if  <  {
			 = 
			 = 
		} else {
			 = 
		}
	}
	 := &.zone[[].index]
	 = .name
	 = .offset
	 = [].when
	// end = maintained during the search
	 = .isDST

	// 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.
func ( *Location) () int {
	// 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 time 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 tzset( string, ,  int64) ( string,  int, ,  int64, ,  bool) {
	var (
		,      string
		,  int
	)

	, ,  = tzsetName()
	if  {
		, ,  = tzsetOffset()
	}
	if ! {
		return "", 0, 0, 0, false, 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.
	 = -

	if len() == 0 || [0] == ',' {
		// No daylight savings time.
		return , , , omega, false, true
	}

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

	if len() == 0 {
		// 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, false
	}
	 = [1:]

	var ,  rule
	, ,  = tzsetRule()
	if ! || len() == 0 || [0] != ',' {
		return "", 0, 0, 0, false, false
	}
	 = [1:]
	, ,  = tzsetRule()
	if ! || len() > 0 {
		return "", 0, 0, 0, false, 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(, , ))
	,  := true, false
	// Note: this is a flipping of "DST" and "STD" while retaining the labels
	// This happens in southern hemispheres. The labelling here thus is a little
	// inconsistent with the goal.
	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 tzsetName( 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 tzsetOffset( 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.
type ruleKind int

const (
	ruleJulian ruleKind = iota
	ruleDOY
	ruleMonthWeekDay
)

// 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 tzsetRule( 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 tzsetNum( 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 tzruleTime( int,  rule,  int) int {
	var  int
	switch .kind {
	case ruleJulian:
		 = (.day - 1) * secondsPerDay
		if isLeap() && .day >= 60 {
			 += secondsPerDay
		}
	case ruleDOY:
		 = .day * secondsPerDay
	case ruleMonthWeekDay:
		// 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".
//
// LoadLocation looks for the IANA Time Zone database in the following
// locations in order:
//
//   - the directory or uncompressed zip file named by the ZONEINFO environment variable
//   - on a Unix system, the system standard installation location
//   - $GOROOT/lib/time/zoneinfo.zip
//   - the time/tzdata package, if it was imported
func ( string) (*Location, error) {
	if  == "" ||  == "UTC" {
		return UTC, nil
	}
	if  == "Local" {
		return Local, nil
	}
	if containsDotDot() || [0] == '/' || [0] == '\\' {
		// 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(, platformZoneSources);  == nil {
		return , nil
	} else if  == nil {
		 = 
	}
	return nil, 
}

// containsDotDot reports whether s contains "..".
func containsDotDot( string) bool {
	if len() < 2 {
		return false
	}
	for  := 0;  < len()-1; ++ {
		if [] == '.' && [+1] == '.' {
			return true
		}
	}
	return false
}