// Copyright 2010 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 (_// for linkname)// These are predefined layouts for use in [Time.Format] and [time.Parse].// The reference time used in these layouts is the specific time stamp://// 01/02 03:04:05PM '06 -0700//// (January 2, 15:04:05, 2006, in time zone seven hours west of GMT).// That value is recorded as the constant named [Layout], listed below. As a Unix// time, this is 1136239445. Since MST is GMT-0700, the reference would be// printed by the Unix date command as://// Mon Jan 2 15:04:05 MST 2006//// It is a regrettable historic error that the date uses the American convention// of putting the numerical month before the day.//// The example for Time.Format demonstrates the working of the layout string// in detail and is a good reference.//// Note that the [RFC822], [RFC850], and [RFC1123] formats should be applied// only to local times. Applying them to UTC times will use "UTC" as the// time zone abbreviation, while strictly speaking those RFCs require the// use of "GMT" in that case.// When using the [RFC1123] or [RFC1123Z] formats for parsing, note that these// formats define a leading zero for the day-in-month portion, which is not// strictly allowed by RFC 1123. This will result in an error when parsing// date strings that occur in the first 9 days of a given month.// In general [RFC1123Z] should be used instead of [RFC1123] for servers// that insist on that format, and [RFC3339] should be preferred for new protocols.// [RFC3339], [RFC822], [RFC822Z], [RFC1123], and [RFC1123Z] are useful for formatting;// when used with time.Parse they do not accept all the time formats// permitted by the RFCs and they do accept time formats not formally defined.// The [RFC3339Nano] format removes trailing zeros from the seconds field// and thus may not sort correctly once formatted.//// Most programs can use one of the defined constants as the layout passed to// Format or Parse. The rest of this comment can be ignored unless you are// creating a custom layout string.//// To define your own format, write down what the reference time would look like// formatted your way; see the values of constants like [ANSIC], [StampMicro] or// [Kitchen] for examples. The model is to demonstrate what the reference time// looks like so that the Format and Parse methods can apply the same// transformation to a general time value.//// Here is a summary of the components of a layout string. Each element shows by// example the formatting of an element of the reference time. Only these values// are recognized. Text in the layout string that is not recognized as part of// the reference time is echoed verbatim during Format and expected to appear// verbatim in the input to Parse.//// Year: "2006" "06"// Month: "Jan" "January" "01" "1"// Day of the week: "Mon" "Monday"// Day of the month: "2" "_2" "02"// Day of the year: "__2" "002"// Hour: "15" "3" "03" (PM or AM)// Minute: "4" "04"// Second: "5" "05"// AM/PM mark: "PM"//// Numeric time zone offsets format as follows://// "-0700" ±hhmm// "-07:00" ±hh:mm// "-07" ±hh// "-070000" ±hhmmss// "-07:00:00" ±hh:mm:ss//// Replacing the sign in the format with a Z triggers// the ISO 8601 behavior of printing Z instead of an// offset for the UTC zone. Thus://// "Z0700" Z or ±hhmm// "Z07:00" Z or ±hh:mm// "Z07" Z or ±hh// "Z070000" Z or ±hhmmss// "Z07:00:00" Z or ±hh:mm:ss//// Within the format string, the underscores in "_2" and "__2" represent spaces// that may be replaced by digits if the following number has multiple digits,// for compatibility with fixed-width Unix time formats. A leading zero represents// a zero-padded value.//// The formats __2 and 002 are space-padded and zero-padded// three-character day of year; there is no unpadded day of year format.//// A comma or decimal point followed by one or more zeros represents// a fractional second, printed to the given number of decimal places.// A comma or decimal point followed by one or more nines represents// a fractional second, printed to the given number of decimal places, with// trailing zeros removed.// For example "15:04:05,000" or "15:04:05.000" formats or parses with// millisecond precision.//// Some valid layouts are invalid time values for time.Parse, due to formats// such as _ for space padding and Z for zone information.const (Layout = "01/02 03:04:05PM '06 -0700"// The reference time, in numerical order.ANSIC = "Mon Jan _2 15:04:05 2006"UnixDate = "Mon Jan _2 15:04:05 MST 2006"RubyDate = "Mon Jan 02 15:04:05 -0700 2006"RFC822 = "02 Jan 06 15:04 MST"RFC822Z = "02 Jan 06 15:04 -0700"// RFC822 with numeric zoneRFC850 = "Monday, 02-Jan-06 15:04:05 MST"RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700"// RFC1123 with numeric zoneRFC3339 = "2006-01-02T15:04:05Z07:00"RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"Kitchen = "3:04PM"// Handy time stamps.Stamp = "Jan _2 15:04:05"StampMilli = "Jan _2 15:04:05.000"StampMicro = "Jan _2 15:04:05.000000"StampNano = "Jan _2 15:04:05.000000000"DateTime = "2006-01-02 15:04:05"DateOnly = "2006-01-02"TimeOnly = "15:04:05")const ( _ = iota stdLongMonth = iota + stdNeedDate// "January" stdMonth // "Jan" stdNumMonth // "1" stdZeroMonth // "01" stdLongWeekDay // "Monday" stdWeekDay // "Mon" stdDay // "2" stdUnderDay // "_2" stdZeroDay // "02" stdUnderYearDay = iota + stdNeedYday// "__2" stdZeroYearDay // "002" stdHour = iota + stdNeedClock// "15" stdHour12 // "3" stdZeroHour12 // "03" stdMinute // "4" stdZeroMinute // "04" stdSecond // "5" stdZeroSecond // "05" stdLongYear = iota + stdNeedDate// "2006" stdYear // "06" stdPM = iota + stdNeedClock// "PM" stdpm // "pm" stdTZ = iota// "MST" stdISO8601TZ // "Z0700" // prints Z for UTC stdISO8601SecondsTZ // "Z070000" stdISO8601ShortTZ // "Z07" stdISO8601ColonTZ // "Z07:00" // prints Z for UTC stdISO8601ColonSecondsTZ // "Z07:00:00" stdNumTZ // "-0700" // always numeric stdNumSecondsTz // "-070000" stdNumShortTZ // "-07" // always numeric stdNumColonTZ // "-07:00" // always numeric stdNumColonSecondsTZ // "-07:00:00" stdFracSecond0 // ".0", ".00", ... , trailing zeros included stdFracSecond9 // ".9", ".99", ..., trailing zeros omitted stdNeedDate = 1 << 8// need month, day, year stdNeedYday = 1 << 9// need yday stdNeedClock = 1 << 10// need hour, minute, second stdArgShift = 16// extra argument in high bits, above low stdArgShift stdSeparatorShift = 28// extra argument in high 4 bits for fractional second separators stdMask = 1<<stdArgShift - 1// mask out argument)// std0x records the std values for "01", "02", ..., "06".var std0x = [...]int{stdZeroMonth, stdZeroDay, stdZeroHour12, stdZeroMinute, stdZeroSecond, stdYear}// startsWithLowerCase reports whether the string has a lower-case letter at the beginning.// Its purpose is to prevent matching strings like "Month" when looking for "Mon".func startsWithLowerCase( string) bool {iflen() == 0 {returnfalse } := [0]return'a' <= && <= 'z'}// nextStdChunk finds the first occurrence of a std string in// layout and returns the text before, the std string, and the text after.//// nextStdChunk should be an internal detail,// but widely used packages access it using linkname.// Notable members of the hall of shame include:// - github.com/searKing/golang/go//// Do not remove or change the type signature.// See go.dev/issue/67401.////go:linkname nextStdChunkfunc nextStdChunk( string) ( string, int, string) {for := 0; < len(); ++ {switch := int([]); {case'J': // January, Janiflen() >= +3 && [:+3] == "Jan" {iflen() >= +7 && [:+7] == "January" {return [0:], stdLongMonth, [+7:] }if !startsWithLowerCase([+3:]) {return [0:], stdMonth, [+3:] } }case'M': // Monday, Mon, MSTiflen() >= +3 {if [:+3] == "Mon" {iflen() >= +6 && [:+6] == "Monday" {return [0:], stdLongWeekDay, [+6:] }if !startsWithLowerCase([+3:]) {return [0:], stdWeekDay, [+3:] } }if [:+3] == "MST" {return [0:], stdTZ, [+3:] } }case'0': // 01, 02, 03, 04, 05, 06, 002iflen() >= +2 && '1' <= [+1] && [+1] <= '6' {return [0:], std0x[[+1]-'1'], [+2:] }iflen() >= +3 && [+1] == '0' && [+2] == '2' {return [0:], stdZeroYearDay, [+3:] }case'1': // 15, 1iflen() >= +2 && [+1] == '5' {return [0:], stdHour, [+2:] }return [0:], stdNumMonth, [+1:]case'2': // 2006, 2iflen() >= +4 && [:+4] == "2006" {return [0:], stdLongYear, [+4:] }return [0:], stdDay, [+1:]case'_': // _2, _2006, __2iflen() >= +2 && [+1] == '2' {// _2006 is really a literal _, followed by stdLongYeariflen() >= +5 && [+1:+5] == "2006" {return [0 : +1], stdLongYear, [+5:] }return [0:], stdUnderDay, [+2:] }iflen() >= +3 && [+1] == '_' && [+2] == '2' {return [0:], stdUnderYearDay, [+3:] }case'3':return [0:], stdHour12, [+1:]case'4':return [0:], stdMinute, [+1:]case'5':return [0:], stdSecond, [+1:]case'P': // PMiflen() >= +2 && [+1] == 'M' {return [0:], stdPM, [+2:] }case'p': // pmiflen() >= +2 && [+1] == 'm' {return [0:], stdpm, [+2:] }case'-': // -070000, -07:00:00, -0700, -07:00, -07iflen() >= +7 && [:+7] == "-070000" {return [0:], stdNumSecondsTz, [+7:] }iflen() >= +9 && [:+9] == "-07:00:00" {return [0:], stdNumColonSecondsTZ, [+9:] }iflen() >= +5 && [:+5] == "-0700" {return [0:], stdNumTZ, [+5:] }iflen() >= +6 && [:+6] == "-07:00" {return [0:], stdNumColonTZ, [+6:] }iflen() >= +3 && [:+3] == "-07" {return [0:], stdNumShortTZ, [+3:] }case'Z': // Z070000, Z07:00:00, Z0700, Z07:00,iflen() >= +7 && [:+7] == "Z070000" {return [0:], stdISO8601SecondsTZ, [+7:] }iflen() >= +9 && [:+9] == "Z07:00:00" {return [0:], stdISO8601ColonSecondsTZ, [+9:] }iflen() >= +5 && [:+5] == "Z0700" {return [0:], stdISO8601TZ, [+5:] }iflen() >= +6 && [:+6] == "Z07:00" {return [0:], stdISO8601ColonTZ, [+6:] }iflen() >= +3 && [:+3] == "Z07" {return [0:], stdISO8601ShortTZ, [+3:] }case'.', ',': // ,000, or .000, or ,999, or .999 - repeated digits for fractional seconds.if +1 < len() && ([+1] == '0' || [+1] == '9') { := [+1] := + 1for < len() && [] == { ++ }// String of digits must end here - only fractional second is all digits.if !isDigit(, ) { := stdFracSecond0if [+1] == '9' { = stdFracSecond9 } := stdFracSecond(, -(+1), )return [0:], , [:] } } } }return , 0, ""}var longDayNames = []string{"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday",}var shortDayNames = []string{"Sun","Mon","Tue","Wed","Thu","Fri","Sat",}var shortMonthNames = []string{"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",}var longMonthNames = []string{"January","February","March","April","May","June","July","August","September","October","November","December",}// match reports whether s1 and s2 match ignoring case.// It is assumed s1 and s2 are the same length.func match(, string) bool {for := 0; < len(); ++ { := [] := []if != {// Switch to lower-case; 'a'-'A' is known to be a single bit. |= 'a' - 'A' |= 'a' - 'A'if != || < 'a' || > 'z' {returnfalse } } }returntrue}func lookup( []string, string) (int, string, error) {for , := range {iflen() >= len() && match([:len()], ) {return , [len():], nil } }return -1, , errBad}// appendInt appends the decimal form of x to b and returns the result.// If the decimal form (excluding sign) is shorter than width, the result is padded with leading 0's.// Duplicates functionality in strconv, but avoids dependency.func appendInt( []byte, int, int) []byte { := uint()if < 0 { = append(, '-') = uint(-) }// 2-digit and 4-digit fields are the most common in time formats. := func( uint) byte { return'0' + byte() }switch {case == 2 && < 1e2:returnappend(, (/1e1), (%1e1))case == 4 && < 1e4:returnappend(, (/1e3), (/1e2%1e1), (/1e1%1e1), (%1e1)) }// Compute the number of decimal digits.varintif == 0 { = 1 }for := ; > 0; /= 10 { ++ }// Add 0-padding.for := - ; > 0; -- { = append(, '0') }// Ensure capacity.iflen()+ <= cap() { = [:len()+] } else { = append(, make([]byte, )...) }// Assemble decimal in reverse order. := len() - 1for >= 10 && > 0 { := / 10 [] = ( - *10) = -- } [] = ()return}// Never printed, just needs to be non-nil for return by atoi.var errAtoi = errors.New("time: invalid number")// Duplicates functionality in strconv, but avoids dependency.func atoi[ []byte | string]( ) ( int, error) { := falseiflen() > 0 && ([0] == '-' || [0] == '+') { = [0] == '-' = [1:] } , , := leadingInt() = int()if != nil || len() > 0 {return0, errAtoi }if { = - }return , nil}// The "std" value passed to appendNano contains two packed fields: the number of// digits after the decimal and the separator character (period or comma).// These functions pack and unpack that variable.func stdFracSecond(, , int) int {// Use 0xfff to make the failure case even more absurd.if == '.' {return | (( & 0xfff) << stdArgShift) }return | (( & 0xfff) << stdArgShift) | 1<<stdSeparatorShift}func digitsLen( int) int {return ( >> stdArgShift) & 0xfff}func separator( int) byte {if ( >> stdSeparatorShift) == 0 {return'.' }return','}// appendNano appends a fractional second, as nanoseconds, to b// and returns the result. The nanosec must be within [0, 999999999].func appendNano( []byte, int, int) []byte { := &stdMask == stdFracSecond9 := digitsLen()if && ( == 0 || == 0) {return } := separator() = append(, ) = appendInt(, , 9)if < 9 { = [:len()-9+] }if {forlen() > 0 && [len()-1] == '0' { = [:len()-1] }iflen() > 0 && [len()-1] == { = [:len()-1] } }return}// String returns the time formatted using the format string//// "2006-01-02 15:04:05.999999999 -0700 MST"//// If the time has a monotonic clock reading, the returned string// includes a final field "m=±<value>", where value is the monotonic// clock reading formatted as a decimal number of seconds.//// The returned string is meant for debugging; for a stable serialized// representation, use t.MarshalText, t.MarshalBinary, or t.Format// with an explicit format string.func ( Time) () string { := .Format("2006-01-02 15:04:05.999999999 -0700 MST")// Format monotonic clock reading as m=±ddd.nnnnnnnnn.if .wall&hasMonotonic != 0 { := uint64(.ext) := byte('+')if .ext < 0 { = '-' = - } , := /1e9, %1e9 , := /1e9, %1e9 := make([]byte, 0, 24) = append(, " m="...) = append(, ) := 0if != 0 { = appendInt(, int(), 0) = 9 } = appendInt(, int(), ) = append(, '.') = appendInt(, int(), 9) += string() }return}// GoString implements [fmt.GoStringer] and formats t to be printed in Go source// code.func ( Time) () string { := .absSec() , , := .days().date() , , := .clock() := make([]byte, 0, len("time.Date(9999, time.September, 31, 23, 59, 59, 999999999, time.Local)")) = append(, "time.Date("...) = appendInt(, , 0)ifJanuary <= && <= December { = append(, ", time."...) = append(, longMonthNames[-1]...) } else {// It's difficult to construct a time.Time with a date outside the // standard range but we might as well try to handle the case. = appendInt(, int(), 0) } = append(, ", "...) = appendInt(, , 0) = append(, ", "...) = appendInt(, , 0) = append(, ", "...) = appendInt(, , 0) = append(, ", "...) = appendInt(, , 0) = append(, ", "...) = appendInt(, .Nanosecond(), 0) = append(, ", "...)switch := .Location(); {caseUTC, nil: = append(, "time.UTC"...)caseLocal: = append(, "time.Local"...)default:// there are several options for how we could display this, none of // which are great: // // - use Location(loc.name), which is not technically valid syntax // - use LoadLocation(loc.name), which will cause a syntax error when // embedded and also would require us to escape the string without // importing fmt or strconv // - try to use FixedZone, which would also require escaping the name // and would represent e.g. "America/Los_Angeles" daylight saving time // shifts inaccurately // - use the pointer format, which is no worse than you'd get with the // old fmt.Sprintf("%#v", t) format. // // Of these, Location(loc.name) is the least disruptive. This is an edge // case we hope not to hit too often. = append(, `time.Location(`...) = append(, quote(.name)...) = append(, ')') } = append(, ')')returnstring()}// Format returns a textual representation of the time value formatted according// to the layout defined by the argument. See the documentation for the// constant called [Layout] to see how to represent the layout format.//// The executable example for [Time.Format] demonstrates the working// of the layout string in detail and is a good reference.func ( Time) ( string) string {const = 64var []byte := len() + 10if < {var []byte = [:0] } else { = make([]byte, 0, ) } = .AppendFormat(, )returnstring()}// AppendFormat is like [Time.Format] but appends the textual// representation to b and returns the extended buffer.func ( Time) ( []byte, string) []byte {// Optimize for RFC3339 as it accounts for over half of all representations.switch {caseRFC3339:return .appendFormatRFC3339(, false)caseRFC3339Nano:return .appendFormatRFC3339(, true)default:return .appendFormat(, ) }}func ( Time) ( []byte, string) []byte { , , := .locabs() := .days()var (int = -1Monthintint = -1int = -1intint )// Each iteration generates one std value.for != "" { , , := nextStdChunk()if != "" { = append(, ...) }if == 0 {break } = // Compute year, month, day if needed.if < 0 && &stdNeedDate != 0 { , , = .date() }if < 0 && &stdNeedYday != 0 { _, = .yearYday() }// Compute hour, minute, second if needed.if < 0 && &stdNeedClock != 0 { , , = .clock() }switch & stdMask {casestdYear: := if < 0 { = - } = appendInt(, %100, 2)casestdLongYear: = appendInt(, , 4)casestdMonth: = append(, .String()[:3]...)casestdLongMonth: := .String() = append(, ...)casestdNumMonth: = appendInt(, int(), 0)casestdZeroMonth: = appendInt(, int(), 2)casestdWeekDay: = append(, .weekday().String()[:3]...)casestdLongWeekDay: := .weekday().String() = append(, ...)casestdDay: = appendInt(, , 0)casestdUnderDay:if < 10 { = append(, ' ') } = appendInt(, , 0)casestdZeroDay: = appendInt(, , 2)casestdUnderYearDay:if < 100 { = append(, ' ')if < 10 { = append(, ' ') } } = appendInt(, , 0)casestdZeroYearDay: = appendInt(, , 3)casestdHour: = appendInt(, , 2)casestdHour12:// Noon is 12PM, midnight is 12AM. := % 12if == 0 { = 12 } = appendInt(, , 0)casestdZeroHour12:// Noon is 12PM, midnight is 12AM. := % 12if == 0 { = 12 } = appendInt(, , 2)casestdMinute: = appendInt(, , 0)casestdZeroMinute: = appendInt(, , 2)casestdSecond: = appendInt(, , 0)casestdZeroSecond: = appendInt(, , 2)casestdPM:if >= 12 { = append(, "PM"...) } else { = append(, "AM"...) }casestdpm:if >= 12 { = append(, "pm"...) } else { = append(, "am"...) }casestdISO8601TZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ShortTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumColonTZ, stdNumSecondsTz, stdNumShortTZ, stdNumColonSecondsTZ:// Ugly special case. We cheat and take the "Z" variants // to mean "the time zone as formatted for ISO 8601".if == 0 && ( == stdISO8601TZ || == stdISO8601ColonTZ || == stdISO8601SecondsTZ || == stdISO8601ShortTZ || == stdISO8601ColonSecondsTZ) { = append(, 'Z')break } := / 60// convert to minutes := if < 0 { = append(, '-') = - = - } else { = append(, '+') } = appendInt(, /60, 2)if == stdISO8601ColonTZ || == stdNumColonTZ || == stdISO8601ColonSecondsTZ || == stdNumColonSecondsTZ { = append(, ':') }if != stdNumShortTZ && != stdISO8601ShortTZ { = appendInt(, %60, 2) }// append seconds if appropriateif == stdISO8601SecondsTZ || == stdNumSecondsTz || == stdNumColonSecondsTZ || == stdISO8601ColonSecondsTZ {if == stdNumColonSecondsTZ || == stdISO8601ColonSecondsTZ { = append(, ':') } = appendInt(, %60, 2) }casestdTZ:if != "" { = append(, ...)break }// No time zone known for this time, but we must print one. // Use the -0700 format. := / 60// convert to minutesif < 0 { = append(, '-') = - } else { = append(, '+') } = appendInt(, /60, 2) = appendInt(, %60, 2)casestdFracSecond0, stdFracSecond9: = appendNano(, .Nanosecond(), ) } }return}var errBad = errors.New("bad value for field") // placeholder not passed to user// ParseError describes a problem parsing a time string.typeParseErrorstruct { Layout string Value string LayoutElem string ValueElem string Message string}// newParseError creates a new ParseError.// The provided value and valueElem are cloned to avoid escaping their values.func newParseError(, , , , string) *ParseError { := stringslite.Clone() := stringslite.Clone()return &ParseError{, , , , }}// These are borrowed from unicode/utf8 and strconv and replicate behavior in// that package, since we can't take a dependency on either.const ( lowerhex = "0123456789abcdef" runeSelf = 0x80 runeError = '\uFFFD')func quote( string) string { := make([]byte, 1, len()+2) // slice will be at least len(s) + quotes [0] = '"'for , := range {if >= runeSelf || < ' ' {// This means you are asking us to parse a time.Duration or // time.Location with unprintable or non-ASCII characters in it. // We don't expect to hit this case very often. We could try to // reproduce strconv.Quote's behavior with full fidelity but // given how rarely we expect to hit these edge cases, speed and // conciseness are better.varintif == runeError { = 1if +2 < len() && [:+3] == string(runeError) { = 3 } } else { = len(string()) }for := 0; < ; ++ { = append(, `\x`...) = append(, lowerhex[[+]>>4]) = append(, lowerhex[[+]&0xF]) } } else {if == '"' || == '\\' { = append(, '\\') } = append(, string()...) } } = append(, '"')returnstring()}// Error returns the string representation of a ParseError.func ( *ParseError) () string {if .Message == "" {return"parsing time " +quote(.Value) + " as " +quote(.Layout) + ": cannot parse " +quote(.ValueElem) + " as " +quote(.LayoutElem) }return"parsing time " +quote(.Value) + .Message}// isDigit reports whether s[i] is in range and is a decimal digit.func isDigit[ []byte | string]( , int) bool {iflen() <= {returnfalse } := []return'0' <= && <= '9'}// getnum parses s[0:1] or s[0:2] (fixed forces s[0:2])// as a decimal integer and returns the integer and the// remainder of the string.func getnum( string, bool) (int, string, error) {if !isDigit(, 0) {return0, , errBad }if !isDigit(, 1) {if {return0, , errBad }returnint([0] - '0'), [1:], nil }returnint([0]-'0')*10 + int([1]-'0'), [2:], nil}// getnum3 parses s[0:1], s[0:2], or s[0:3] (fixed forces s[0:3])// as a decimal integer and returns the integer and the remainder// of the string.func getnum3( string, bool) (int, string, error) {var , intfor = 0; < 3 && isDigit(, ); ++ { = *10 + int([]-'0') }if == 0 || && != 3 {return0, , errBad }return , [:], nil}func cutspace( string) string {forlen() > 0 && [0] == ' ' { = [1:] }return}// skip removes the given prefix from value,// treating runs of space characters as equivalent.func skip(, string) (string, error) {forlen() > 0 {if [0] == ' ' {iflen() > 0 && [0] != ' ' {return , errBad } = cutspace() = cutspace()continue }iflen() == 0 || [0] != [0] {return , errBad } = [1:] = [1:] }return , nil}// Parse parses a formatted string and returns the time value it represents.// See the documentation for the constant called [Layout] to see how to// represent the format. The second argument must be parseable using// the format string (layout) provided as the first argument.//// The example for [Time.Format] demonstrates the working of the layout string// in detail and is a good reference.//// When parsing (only), the input may contain a fractional second// field immediately after the seconds field, even if the layout does not// signify its presence. In that case either a comma or a decimal point// followed by a maximal series of digits is parsed as a fractional second.// Fractional seconds are truncated to nanosecond precision.//// Elements omitted from the layout are assumed to be zero or, when// zero is impossible, one, so parsing "3:04pm" returns the time// corresponding to Jan 1, year 0, 15:04:00 UTC (note that because the year is// 0, this time is before the zero Time).// Years must be in the range 0000..9999. The day of the week is checked// for syntax but it is otherwise ignored.//// For layouts specifying the two-digit year 06, a value NN >= 69 will be treated// as 19NN and a value NN < 69 will be treated as 20NN.//// The remainder of this comment describes the handling of time zones.//// In the absence of a time zone indicator, Parse returns a time in UTC.//// When parsing a time with a zone offset like -0700, if the offset corresponds// to a time zone used by the current location ([Local]), then Parse uses that// location and zone in the returned time. Otherwise it records the time as// being in a fabricated location with time fixed at the given zone offset.//// When parsing a time with a zone abbreviation like MST, if the zone abbreviation// has a defined offset in the current location, then that offset is used.// The zone abbreviation "UTC" is recognized as UTC regardless of location.// If the zone abbreviation is unknown, Parse records the time as being// in a fabricated location with the given zone abbreviation and a zero offset.// This choice means that such a time can be parsed and reformatted with the// same layout losslessly, but the exact instant used in the representation will// differ by the actual zone offset. To avoid such problems, prefer time layouts// that use a numeric zone offset, or use [ParseInLocation].func (, string) (Time, error) {// Optimize for RFC3339 as it accounts for over half of all representations.if == RFC3339 || == RFC3339Nano {if , := parseRFC3339(, Local); {return , nil } }returnparse(, , UTC, Local)}// ParseInLocation is like Parse but differs in two important ways.// First, in the absence of time zone information, Parse interprets a time as UTC;// ParseInLocation interprets the time as in the given location.// Second, when given a zone offset or abbreviation, Parse tries to match it// against the Local location; ParseInLocation uses the given location.func (, string, *Location) (Time, error) {// Optimize for RFC3339 as it accounts for over half of all representations.if == RFC3339 || == RFC3339Nano {if , := parseRFC3339(, ); {return , nil } }returnparse(, , , )}func parse(, string, , *Location) (Time, error) { , := , := ""// set if a value is out of range := false// do we need to subtract 12 from the hour for midnight? := false// do we need to add 12 to the hour?// Time being constructed.var (intint = -1int = -1int = -1intintintint *Locationint = -1string )// Each iteration processes one std value.for {varerror , , := nextStdChunk() := [len() : len()-len()] , = skip(, )if != nil {returnTime{}, newParseError(, , , , "") }if == 0 {iflen() != 0 {returnTime{}, newParseError(, , "", , ": extra text: "+quote()) }break } = varstring := switch & stdMask {casestdYear:iflen() < 2 { = errBadbreak } , = [0:2], [2:] , = atoi()if != nil {break }if >= 69 { // Unix time starts Dec 31 1969 in some time zones += 1900 } else { += 2000 }casestdLongYear:iflen() < 4 || !isDigit(, 0) { = errBadbreak } , = [0:4], [4:] , = atoi()casestdMonth: , , = lookup(shortMonthNames, ) ++casestdLongMonth: , , = lookup(longMonthNames, ) ++casestdNumMonth, stdZeroMonth: , , = getnum(, == stdZeroMonth)if == nil && ( <= 0 || 12 < ) { = "month" }casestdWeekDay:// Ignore weekday except for error checking. _, , = lookup(shortDayNames, )casestdLongWeekDay: _, , = lookup(longDayNames, )casestdDay, stdUnderDay, stdZeroDay:if == stdUnderDay && len() > 0 && [0] == ' ' { = [1:] } , , = getnum(, == stdZeroDay)// Note that we allow any one- or two-digit day here. // The month, day, year combination is validated after we've completed parsing.casestdUnderYearDay, stdZeroYearDay:for := 0; < 2; ++ {if == stdUnderYearDay && len() > 0 && [0] == ' ' { = [1:] } } , , = getnum3(, == stdZeroYearDay)// Note that we allow any one-, two-, or three-digit year-day here. // The year-day, year combination is validated after we've completed parsing.casestdHour: , , = getnum(, false)if < 0 || 24 <= { = "hour" }casestdHour12, stdZeroHour12: , , = getnum(, == stdZeroHour12)if < 0 || 12 < { = "hour" }casestdMinute, stdZeroMinute: , , = getnum(, == stdZeroMinute)if < 0 || 60 <= { = "minute" }casestdSecond, stdZeroSecond: , , = getnum(, == stdZeroSecond)if != nil {break }if < 0 || 60 <= { = "second"break }// Special case: do we have a fractional second but no // fractional second in the format?iflen() >= 2 && commaOrPeriod([0]) && isDigit(, 1) { _, , _ = nextStdChunk() &= stdMaskif == stdFracSecond0 || == stdFracSecond9 {// Fractional second in the layout; proceed normallybreak }// No fractional second in the layout but we have one in the input. := 2for ; < len() && isDigit(, ); ++ { } , , = parseNanoseconds(, ) = [:] }casestdPM:iflen() < 2 { = errBadbreak } , = [0:2], [2:]switch {case"PM": = truecase"AM": = truedefault: = errBad }casestdpm:iflen() < 2 { = errBadbreak } , = [0:2], [2:]switch {case"pm": = truecase"am": = truedefault: = errBad }casestdISO8601TZ, stdISO8601ShortTZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ColonSecondsTZ:iflen() >= 1 && [0] == 'Z' { = [1:] = UTCbreak }fallthroughcasestdNumTZ, stdNumShortTZ, stdNumColonTZ, stdNumSecondsTz, stdNumColonSecondsTZ:var , , , stringif == stdISO8601ColonTZ || == stdNumColonTZ {iflen() < 6 { = errBadbreak }if [3] != ':' { = errBadbreak } , , , , = [0:1], [1:3], [4:6], "00", [6:] } elseif == stdNumShortTZ || == stdISO8601ShortTZ {iflen() < 3 { = errBadbreak } , , , , = [0:1], [1:3], "00", "00", [3:] } elseif == stdISO8601ColonSecondsTZ || == stdNumColonSecondsTZ {iflen() < 9 { = errBadbreak }if [3] != ':' || [6] != ':' { = errBadbreak } , , , , = [0:1], [1:3], [4:6], [7:9], [9:] } elseif == stdISO8601SecondsTZ || == stdNumSecondsTz {iflen() < 7 { = errBadbreak } , , , , = [0:1], [1:3], [3:5], [5:7], [7:] } else {iflen() < 5 { = errBadbreak } , , , , = [0:1], [1:3], [3:5], "00", [5:] }var , , int , _, = getnum(, true)if == nil { , _, = getnum(, true)if == nil { , _, = getnum(, true) } }// The range test use > rather than >=, // as some people do write offsets of 24 hours // or 60 minutes or 60 seconds.if > 24 { = "time zone offset hour" }if > 60 { = "time zone offset minute" }if > 60 { = "time zone offset second" } = (*60+)*60 + // offset is in secondsswitch [0] {case'+':case'-': = -default: = errBad }casestdTZ:// Does it look like a time zone?iflen() >= 3 && [0:3] == "UTC" { = UTC = [3:]break } , := parseTimeZone()if ! { = errBadbreak } , = [:], [:]casestdFracSecond0:// stdFracSecond0 requires the exact number of digits as specified in // the layout. := 1 + digitsLen()iflen() < { = errBadbreak } , , = parseNanoseconds(, ) = [:]casestdFracSecond9:iflen() < 2 || !commaOrPeriod([0]) || [1] < '0' || '9' < [1] {// Fractional second omitted.break }// Take any number of digits, even more than asked for, // because it is what the stdSecond case would do. := 0for +1 < len() && '0' <= [+1] && [+1] <= '9' { ++ } , , = parseNanoseconds(, 1+) = [1+:] }if != "" {returnTime{}, newParseError(, , , , ": "++" out of range") }if != nil {returnTime{}, newParseError(, , , , "") } }if && < 12 { += 12 } elseif && == 12 { = 0 }// Convert yday to day, month.if >= 0 {varintvarintifisLeap() {if == 31+29 { = int(February) = 29 } elseif > 31+29 { -- } }if < 1 || > 365 {returnTime{}, newParseError(, , "", , ": day-of-year out of range") }if == 0 { = (-1)/31 + 1ifdaysBefore(Month(+1)) < { ++ } = - daysBefore(Month()) }// If month, day already seen, yday's m, d must match. // Otherwise, set them from m, d.if >= 0 && != {returnTime{}, newParseError(, , "", , ": day-of-year does not match month") } = if >= 0 && != {returnTime{}, newParseError(, , "", , ": day-of-year does not match day") } = } else {if < 0 { = int(January) }if < 0 { = 1 } }// Validate the day of the month.if < 1 || > daysIn(Month(), ) {returnTime{}, newParseError(, , "", , ": day out of range") }if != nil {returnDate(, Month(), , , , , , ), nil }if != -1 { := Date(, Month(), , , , , , UTC) .addSec(-int64())// Look for local zone with the given offset. // If that zone was in effect at the given time, use it. , , , , := .lookup(.unixSec())if == && ( == "" || == ) { .setLoc()return , nil }// Otherwise create fake zone to record offset. := stringslite.Clone() // avoid leaking the input value .setLoc(FixedZone(, ))return , nil }if != "" { := Date(, Month(), , , , , , UTC)// Look for local zone with the given offset. // If that zone was in effect at the given time, use it. , := .lookupName(, .unixSec())if { .addSec(-int64()) .setLoc()return , nil }// Otherwise, create fake zone with unknown offset.iflen() > 3 && [:3] == "GMT" { , _ = atoi([3:]) // Guaranteed OK by parseGMT. *= 3600 } := stringslite.Clone() // avoid leaking the input value .setLoc(FixedZone(, ))return , nil }// Otherwise, fall back to default.returnDate(, Month(), , , , , , ), nil}// parseTimeZone parses a time zone string and returns its length. Time zones// are human-generated and unpredictable. We can't do precise error checking.// On the other hand, for a correct parse there must be a time zone at the// beginning of the string, so it's almost always true that there's one// there. We look at the beginning of the string for a run of upper-case letters.// If there are more than 5, it's an error.// If there are 4 or 5 and the last is a T, it's a time zone.// If there are 3, it's a time zone.// Otherwise, other than special cases, it's not a time zone.// GMT is special because it can have an hour offset.func parseTimeZone( string) ( int, bool) {iflen() < 3 {return0, false }// Special case 1: ChST and MeST are the only zones with a lower-case letter.iflen() >= 4 && ([:4] == "ChST" || [:4] == "MeST") {return4, true }// Special case 2: GMT may have an hour offset; treat it specially.if [:3] == "GMT" { = parseGMT()return , true }// Special Case 3: Some time zones are not named, but have +/-00 formatif [0] == '+' || [0] == '-' { = parseSignedOffset() := > 0// parseSignedOffset returns 0 in case of bad inputreturn , }// How many upper-case letters are there? Need at least three, at most five.varintfor = 0; < 6; ++ {if >= len() {break }if := []; < 'A' || 'Z' < {break } }switch {case0, 1, 2, 6:return0, falsecase5: // Must end in T to match.if [4] == 'T' {return5, true }case4:// Must end in T, except one special case.if [3] == 'T' || [:4] == "WITA" {return4, true }case3:return3, true }return0, false}// parseGMT parses a GMT time zone. The input string is known to start "GMT".// The function checks whether that is followed by a sign and a number in the// range -23 through +23 excluding zero.func parseGMT( string) int { = [3:]iflen() == 0 {return3 }return3 + parseSignedOffset()}// parseSignedOffset parses a signed timezone offset (e.g. "+03" or "-04").// The function checks for a signed number in the range -23 through +23 excluding zero.// Returns length of the found offset string or 0 otherwise.func parseSignedOffset( string) int { := [0]if != '-' && != '+' {return0 } , , := leadingInt([1:])// fail if nothing consumed by leadingIntif != nil || [1:] == {return0 }if > 23 {return0 }returnlen() - len()}func commaOrPeriod( byte) bool {return == '.' || == ','}func parseNanoseconds[ []byte | string]( , int) ( int, string, error) {if !commaOrPeriod([0]) { = errBadreturn }if > 10 { = [:10] = 10 }if , = atoi([1:]); != nil {return }if < 0 { = "fractional second"return }// We need nanoseconds, which means scaling by the number // of missing digits in the format, maximum length 10. := 10 - for := 0; < ; ++ { *= 10 }return}var errLeadingInt = errors.New("time: bad [0-9]*") // never printed// leadingInt consumes the leading [0-9]* from s.func leadingInt[ []byte | string]( ) ( uint64, , error) { := 0for ; < len(); ++ { := []if < '0' || > '9' {break }if > 1<<63/10 {// overflowreturn0, , errLeadingInt } = *10 + uint64() - '0'if > 1<<63 {// overflowreturn0, , errLeadingInt } }return , [:], nil}// leadingFraction consumes the leading [0-9]* from s.// It is used only for fractions, so does not return an error on overflow,// it just stops accumulating precision.func leadingFraction( string) ( uint64, float64, string) { := 0 = 1 := falsefor ; < len(); ++ { := []if < '0' || > '9' {break }if {continue }if > (1<<63-1)/10 {// It's possible for overflow to give a positive number, so take care. = truecontinue } := *10 + uint64() - '0'if > 1<<63 { = truecontinue } = *= 10 }return , , [:]}var unitMap = map[string]uint64{"ns": uint64(Nanosecond),"us": uint64(Microsecond),"µs": uint64(Microsecond), // U+00B5 = micro symbol"μs": uint64(Microsecond), // U+03BC = Greek letter mu"ms": uint64(Millisecond),"s": uint64(Second),"m": uint64(Minute),"h": uint64(Hour),}// ParseDuration parses a duration string.// A duration string is a possibly signed sequence of// decimal numbers, each with optional fraction and a unit suffix,// such as "300ms", "-1.5h" or "2h45m".// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".func ( string) (Duration, error) {// [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+ := varuint64 := false// Consume [-+]?if != "" { := [0]if == '-' || == '+' { = == '-' = [1:] } }// Special case: if all that is left is "0", this is zero.if == "0" {return0, nil }if == "" {return0, errors.New("time: invalid duration " + quote()) }for != "" {var ( , uint64// integers before, after decimal pointfloat64 = 1// value = v + f/scale )varerror// The next character must be [0-9.]if !([0] == '.' || '0' <= [0] && [0] <= '9') {return0, errors.New("time: invalid duration " + quote()) }// Consume [0-9]* := len() , , = leadingInt()if != nil {return0, errors.New("time: invalid duration " + quote()) } := != len() // whether we consumed anything before a period// Consume (\.[0-9]*)? := falseif != "" && [0] == '.' { = [1:] := len() , , = leadingFraction() = != len() }if ! && ! {// no digits (e.g. ".s" or "-.s")return0, errors.New("time: invalid duration " + quote()) }// Consume unit. := 0for ; < len(); ++ { := []if == '.' || '0' <= && <= '9' {break } }if == 0 {return0, errors.New("time: missing unit in duration " + quote()) } := [:] = [:] , := unitMap[]if ! {return0, errors.New("time: unknown unit " + quote() + " in duration " + quote()) }if > 1<<63/ {// overflowreturn0, errors.New("time: invalid duration " + quote()) } *= if > 0 {// float64 is needed to be nanosecond accurate for fractions of hours. // v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit) += uint64(float64() * (float64() / ))if > 1<<63 {// overflowreturn0, errors.New("time: invalid duration " + quote()) } } += if > 1<<63 {return0, errors.New("time: invalid duration " + quote()) } }if {return -Duration(), nil }if > 1<<63-1 {return0, errors.New("time: invalid duration " + quote()) }returnDuration(), nil}
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.