// 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 timeimport ()//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.typeLocationstruct { 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.typezonestruct { 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.typezoneTransstruct { 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.MinInt64omega = 1<<63 - 1// math.MaxInt64)// UTC represents Universal Coordinated Time (UTC).varUTC *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.varutcLoc = 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.varLocal *Location = &localLoc// localLoc is separate so that initLocal can initialize// it even if a client has changed Local.varlocalLocLocationvarlocalOncesync.Oncefunc ( *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()iflen(.zone) == 0 { = "UTC" = 0 = alpha = omegareturn }if := .cacheZone; != nil && .cacheStart <= && < .cacheEnd { = .name = .offset = .cacheStart = .cacheEndreturn }iflen(.tx) == 0 || < .tx[0].when { := &.zone[.lookupFirstZone()] = .name = .offset = alphaiflen(.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 := [].whenif < { = = } else { = } } := &.zone[[].index] = .name = .offset = [].when// 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.func ( *Location) () int {// Case 1.if !.firstZoneUsed() {return0 }// Case 2.iflen(.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.return0}// firstZoneUsed reports whether the first zone is used by some// transition.func ( *Location) () bool {for , := range .tx {if .index == 0 {returntrue } }returnfalse}// 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. = -iflen() == 0 || [0] == ',' {// No daylight savings time.return , , , omega, true } , , = tzsetName()if {iflen() == 0 || [0] == ',' { = + secondsPerHour } else { , , = tzsetOffset() = - // as with stdOffset, above } }if ! {return"", 0, 0, 0, false }iflen() == 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 } = [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 } elseif >= {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) {iflen() == 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 } }iflen() < 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) {iflen() == 0 {return0, "", false } := falseif [0] == '+' { = [1:] } elseif [0] == '-' { = [1:] = true }varint , , = tzsetNum(, 0, 24)if ! {return0, "", false } := * secondsPerHouriflen() == 0 || [0] != ':' {if { = - }return , , true }varint , , = tzsetNum([1:], 0, 59)if ! {return0, "", false } += * secondsPerMinuteiflen() == 0 || [0] != ':' {if { = - }return , , true }varint , , = tzsetNum([1:], 0, 59)if ! {return0, "", false } += if { = - }return , , true}// ruleKind is the kinds of rules that can be seen in a tzset string.typeruleKindintconst (ruleJulianruleKind = iotaruleDOYruleMonthWeekDay)// rule is a rule read from a tzset string.typerulestruct { 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) {varruleiflen() == 0 {returnrule{}, "", false } := falseif [0] == 'J' {varint , , = tzsetNum([1:], 1, 365)if ! {returnrule{}, "", false } .kind = ruleJulian .day = } elseif [0] == 'M' {varint , , = tzsetNum([1:], 1, 12)if ! || len() == 0 || [0] != '.' {returnrule{}, "", false }varint , , = tzsetNum([1:], 1, 5)if ! || len() == 0 || [0] != '.' {returnrule{}, "", false }varint , , = tzsetNum([1:], 0, 6)if ! {returnrule{}, "", false } .kind = ruleMonthWeekDay .day = .week = .mon = } else {varint , , = tzsetNum(, 0, 365)if ! {returnrule{}, "", false } .kind = ruleDOY .day = }iflen() == 0 || [0] != '/' { .time = 2 * secondsPerHour// 2am is the defaultreturn , , true } , , := tzsetOffset([1:])if ! || < 0 {returnrule{}, "", 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) {iflen() == 0 {return0, "", false } = 0for , := range {if < '0' || > '9' {if == 0 || < {return0, "", false }return , [:], true } *= 10 += int() - '0'if > {return0, "", false } }if < {return0, "", 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 {varintswitch .kind {caseruleJulian: = (.day - 1) * secondsPerDayifisLeap() && .day >= 60 { += secondsPerDay }caseruleDOY: = .day * secondsPerDaycaseruleMonthWeekDay:// Zeller's Congruence. := (.mon+9)%12 + 1 := if .mon <= 2 { -- } := / 100 := % 100 := ((26*-2)/10 + 1 + + /4 + /4 - 2*) % 7if < 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])ifisLeap() && .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.varerrLocation = errors.New("time: invalid location name")varzoneinfo *stringvarzoneinfoOncesync.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" {returnUTC, nil }if == "Local" {returnLocal, nil }ifcontainsDotDot() || [0] == '/' || [0] == '\\' {// No valid IANA Time Zone name contains a single dot, // much less dot dot. Likewise, none begin with a slash.returnnil, errLocation }zoneinfoOnce.Do(func() { , := syscall.Getenv("ZONEINFO")zoneinfo = & })varerrorif *zoneinfo != "" {if , := loadTzinfoFromDirOrZip(*zoneinfo, ); == nil {if , := LoadLocationFromTZData(, ); == nil {return , nil } = } elseif != syscall.ENOENT { = } }if , := loadLocation(, zoneSources); == nil {return , nil } elseif == nil { = }returnnil, }// containsDotDot reports whether s contains "..".func ( string) bool {iflen() < 2 {returnfalse }for := 0; < len()-1; ++ {if [] == '.' && [+1] == '.' {returntrue } }returnfalse}