// 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.//// 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.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.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).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.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.varLocal *Location = &localLoc// localLoc is separate so that initLocal can initialize// it even if a client has changed Local.var localLoc Locationvar localOnce sync.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}var unnamedFixedZones []*Locationvar 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 = 12const = 14 := / 60 / 60if == "" && - <= && <= + && *60*60 == {unnamedFixedZonesOnce.Do(func() {unnamedFixedZones = make([]*Location, +1+)for := -; <= +; ++ {unnamedFixedZones[+] = fixedZone("", *60*60) } })returnunnamedFixedZones[+] }returnfixedZone(, )}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()iflen(.zone) == 0 { = "UTC" = 0 = alpha = omega = falsereturn }if := .cacheZone; != nil && .cacheStart <= && < .cacheEnd { = .name = .offset = .cacheStart = .cacheEnd = .isDSTreturn }iflen(.tx) == 0 || < .tx[0].when { := &.zone[.lookupFirstZone()] = .name = .offset = alphaiflen(.tx) > 0 { = .tx[0].when } else { = omega } = .isDSTreturn }// Binary search for entry with largest time <= sec. // Not using sort.Search to avoid dependencies. := .tx = omega := 0 := len()for - > 1 { := int(uint(+) >> 1) := [].whenif < { = = } 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() {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 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. = -iflen() == 0 || [0] == ',' {// No daylight savings time.return , , , omega, false, true } , , = tzsetName()if {iflen() == 0 || [0] == ',' { = + secondsPerHour } else { , , = tzsetOffset() = - // as with stdOffset, above } }if ! {return"", 0, 0, 0, false, 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, 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 }// Compute start of year in seconds since Unix epoch, // and seconds since then to get to sec. , := absSeconds( + unixToInternal + internalToAbsolute).days().yearYday() := int64((-1)*secondsPerDay) + %secondsPerDay := - := 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 } 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 tzsetName( 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 tzsetOffset( string) ( int, string, bool) {iflen() == 0 {return0, "", false } := falseif [0] == '+' { = [1:] } elseif [0] == '-' { = [1:] = true }// The tzdata code permits values up to 24 * 7 here, // although POSIX does not.varint , , = tzsetNum(, 0, 24*7)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.type ruleKind intconst ( 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) {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 ! {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 tzsetNum( 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 tzruleTime( 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(Month(.mon)))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.var errLocation = errors.New("time: invalid location name")var zoneinfo *stringvar 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 importedfunc ( 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(, platformZoneSources); == nil {return , nil } elseif == nil { = }returnnil, }// containsDotDot reports whether s contains "..".func containsDotDot( string) bool {iflen() < 2 {returnfalse }for := 0; < len()-1; ++ {if [] == '.' && [+1] == '.' {returntrue } }returnfalse}
The pages are generated with Goldsv0.7.3. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.