1 // Copyright 2010 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
9 // These are predefined layouts for use in Time.Format and Time.Parse.
10 // The reference time used in the layouts is the specific time:
11 // Mon Jan 2 15:04:05 MST 2006
12 // which is Unix time 1136239445. Since MST is GMT-0700,
13 // the reference time can be thought of as
14 // 01/02 03:04:05PM '06 -0700
15 // To define your own format, write down what the reference time would look
16 // like formatted your way; see the values of constants like ANSIC,
17 // StampMicro or Kitchen for examples. The model is to demonstrate what the
18 // reference time looks like so that the Format and Parse methods can apply
19 // the same transformation to a general time value.
21 // Within the format string, an underscore _ represents a space that may be
22 // replaced by a digit if the following number (a day) has two digits; for
23 // compatibility with fixed-width Unix time formats.
25 // A decimal point followed by one or more zeros represents a fractional
26 // second, printed to the given number of decimal places. A decimal point
27 // followed by one or more nines represents a fractional second, printed to
28 // the given number of decimal places, with trailing zeros removed.
29 // When parsing (only), the input may contain a fractional second
30 // field immediately after the seconds field, even if the layout does not
31 // signify its presence. In that case a decimal point followed by a maximal
32 // series of digits is parsed as a fractional second.
34 // Numeric time zone offsets format as follows:
38 // Replacing the sign in the format with a Z triggers
39 // the ISO 8601 behavior of printing Z instead of an
40 // offset for the UTC zone. Thus:
45 // The executable example for time.Format demonstrates the working
46 // of the layout string in detail and is a good reference.
48 // Note that the RFC822, RFC850, and RFC1123 formats should be applied
49 // only to local times. Applying them to UTC times will use "UTC" as the
50 // time zone abbreviation, while strictly speaking those RFCs require the
51 // use of "GMT" in that case.
52 // In general RFC1123Z should be used instead of RFC1123 for servers
53 // that insist on that format, and RFC3339 should be preferred for new protocols.
54 // RFC822, RFC822Z, RFC1123, and RFC1123Z are useful for formatting;
55 // when used with time.Parse they do not accept all the time formats
56 // permitted by the RFCs.
58 ANSIC
= "Mon Jan _2 15:04:05 2006"
59 UnixDate
= "Mon Jan _2 15:04:05 MST 2006"
60 RubyDate
= "Mon Jan 02 15:04:05 -0700 2006"
61 RFC822
= "02 Jan 06 15:04 MST"
62 RFC822Z
= "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
63 RFC850
= "Monday, 02-Jan-06 15:04:05 MST"
64 RFC1123
= "Mon, 02 Jan 2006 15:04:05 MST"
65 RFC1123Z
= "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
66 RFC3339
= "2006-01-02T15:04:05Z07:00"
67 RFC3339Nano
= "2006-01-02T15:04:05.999999999Z07:00"
70 Stamp
= "Jan _2 15:04:05"
71 StampMilli
= "Jan _2 15:04:05.000"
72 StampMicro
= "Jan _2 15:04:05.000000"
73 StampNano
= "Jan _2 15:04:05.000000000"
78 stdLongMonth
= iota + stdNeedDate
// "January"
82 stdLongWeekDay
// "Monday"
87 stdHour
= iota + stdNeedClock
// "15"
94 stdLongYear
= iota + stdNeedDate
// "2006"
96 stdPM
= iota + stdNeedClock
// "PM"
99 stdISO8601TZ
// "Z0700" // prints Z for UTC
100 stdISO8601SecondsTZ
// "Z070000"
101 stdISO8601ShortTZ
// "Z07"
102 stdISO8601ColonTZ
// "Z07:00" // prints Z for UTC
103 stdISO8601ColonSecondsTZ
// "Z07:00:00"
104 stdNumTZ
// "-0700" // always numeric
105 stdNumSecondsTz
// "-070000"
106 stdNumShortTZ
// "-07" // always numeric
107 stdNumColonTZ
// "-07:00" // always numeric
108 stdNumColonSecondsTZ
// "-07:00:00"
109 stdFracSecond0
// ".0", ".00", ... , trailing zeros included
110 stdFracSecond9
// ".9", ".99", ..., trailing zeros omitted
112 stdNeedDate
= 1 << 8 // need month, day, year
113 stdNeedClock
= 2 << 8 // need hour, minute, second
114 stdArgShift
= 16 // extra argument in high bits, above low stdArgShift
115 stdMask
= 1<<stdArgShift
- 1 // mask out argument
118 // std0x records the std values for "01", "02", ..., "06".
119 var std0x
= [...]int{stdZeroMonth
, stdZeroDay
, stdZeroHour12
, stdZeroMinute
, stdZeroSecond
, stdYear
}
121 // startsWithLowerCase reports whether the string has a lower-case letter at the beginning.
122 // Its purpose is to prevent matching strings like "Month" when looking for "Mon".
123 func startsWithLowerCase(str
string) bool {
128 return 'a' <= c
&& c
<= 'z'
131 // nextStdChunk finds the first occurrence of a std string in
132 // layout and returns the text before, the std string, and the text after.
133 func nextStdChunk(layout
string) (prefix
string, std
int, suffix
string) {
134 for i
:= 0; i
< len(layout
); i
++ {
135 switch c
:= int(layout
[i
]); c
{
136 case 'J': // January, Jan
137 if len(layout
) >= i
+3 && layout
[i
:i
+3] == "Jan" {
138 if len(layout
) >= i
+7 && layout
[i
:i
+7] == "January" {
139 return layout
[0:i
], stdLongMonth
, layout
[i
+7:]
141 if !startsWithLowerCase(layout
[i
+3:]) {
142 return layout
[0:i
], stdMonth
, layout
[i
+3:]
146 case 'M': // Monday, Mon, MST
147 if len(layout
) >= i
+3 {
148 if layout
[i
:i
+3] == "Mon" {
149 if len(layout
) >= i
+6 && layout
[i
:i
+6] == "Monday" {
150 return layout
[0:i
], stdLongWeekDay
, layout
[i
+6:]
152 if !startsWithLowerCase(layout
[i
+3:]) {
153 return layout
[0:i
], stdWeekDay
, layout
[i
+3:]
156 if layout
[i
:i
+3] == "MST" {
157 return layout
[0:i
], stdTZ
, layout
[i
+3:]
161 case '0': // 01, 02, 03, 04, 05, 06
162 if len(layout
) >= i
+2 && '1' <= layout
[i
+1] && layout
[i
+1] <= '6' {
163 return layout
[0:i
], std0x
[layout
[i
+1]-'1'], layout
[i
+2:]
167 if len(layout
) >= i
+2 && layout
[i
+1] == '5' {
168 return layout
[0:i
], stdHour
, layout
[i
+2:]
170 return layout
[0:i
], stdNumMonth
, layout
[i
+1:]
173 if len(layout
) >= i
+4 && layout
[i
:i
+4] == "2006" {
174 return layout
[0:i
], stdLongYear
, layout
[i
+4:]
176 return layout
[0:i
], stdDay
, layout
[i
+1:]
178 case '_': // _2, _2006
179 if len(layout
) >= i
+2 && layout
[i
+1] == '2' {
180 //_2006 is really a literal _, followed by stdLongYear
181 if len(layout
) >= i
+5 && layout
[i
+1:i
+5] == "2006" {
182 return layout
[0 : i
+1], stdLongYear
, layout
[i
+5:]
184 return layout
[0:i
], stdUnderDay
, layout
[i
+2:]
188 return layout
[0:i
], stdHour12
, layout
[i
+1:]
191 return layout
[0:i
], stdMinute
, layout
[i
+1:]
194 return layout
[0:i
], stdSecond
, layout
[i
+1:]
197 if len(layout
) >= i
+2 && layout
[i
+1] == 'M' {
198 return layout
[0:i
], stdPM
, layout
[i
+2:]
202 if len(layout
) >= i
+2 && layout
[i
+1] == 'm' {
203 return layout
[0:i
], stdpm
, layout
[i
+2:]
206 case '-': // -070000, -07:00:00, -0700, -07:00, -07
207 if len(layout
) >= i
+7 && layout
[i
:i
+7] == "-070000" {
208 return layout
[0:i
], stdNumSecondsTz
, layout
[i
+7:]
210 if len(layout
) >= i
+9 && layout
[i
:i
+9] == "-07:00:00" {
211 return layout
[0:i
], stdNumColonSecondsTZ
, layout
[i
+9:]
213 if len(layout
) >= i
+5 && layout
[i
:i
+5] == "-0700" {
214 return layout
[0:i
], stdNumTZ
, layout
[i
+5:]
216 if len(layout
) >= i
+6 && layout
[i
:i
+6] == "-07:00" {
217 return layout
[0:i
], stdNumColonTZ
, layout
[i
+6:]
219 if len(layout
) >= i
+3 && layout
[i
:i
+3] == "-07" {
220 return layout
[0:i
], stdNumShortTZ
, layout
[i
+3:]
223 case 'Z': // Z070000, Z07:00:00, Z0700, Z07:00,
224 if len(layout
) >= i
+7 && layout
[i
:i
+7] == "Z070000" {
225 return layout
[0:i
], stdISO8601SecondsTZ
, layout
[i
+7:]
227 if len(layout
) >= i
+9 && layout
[i
:i
+9] == "Z07:00:00" {
228 return layout
[0:i
], stdISO8601ColonSecondsTZ
, layout
[i
+9:]
230 if len(layout
) >= i
+5 && layout
[i
:i
+5] == "Z0700" {
231 return layout
[0:i
], stdISO8601TZ
, layout
[i
+5:]
233 if len(layout
) >= i
+6 && layout
[i
:i
+6] == "Z07:00" {
234 return layout
[0:i
], stdISO8601ColonTZ
, layout
[i
+6:]
236 if len(layout
) >= i
+3 && layout
[i
:i
+3] == "Z07" {
237 return layout
[0:i
], stdISO8601ShortTZ
, layout
[i
+3:]
240 case '.': // .000 or .999 - repeated digits for fractional seconds.
241 if i
+1 < len(layout
) && (layout
[i
+1] == '0' || layout
[i
+1] == '9') {
244 for j
< len(layout
) && layout
[j
] == ch
{
247 // String of digits must end here - only fractional second is all digits.
248 if !isDigit(layout
, j
) {
249 std
:= stdFracSecond0
250 if layout
[i
+1] == '9' {
253 std |
= (j
- (i
+ 1)) << stdArgShift
254 return layout
[0:i
], std
, layout
[j
:]
262 var longDayNames
= []string{
272 var shortDayNames
= []string{
282 var shortMonthNames
= []string{
298 var longMonthNames
= []string{
314 // match reports whether s1 and s2 match ignoring case.
315 // It is assumed s1 and s2 are the same length.
316 func match(s1
, s2
string) bool {
317 for i
:= 0; i
< len(s1
); i
++ {
321 // Switch to lower-case; 'a'-'A' is known to be a single bit.
324 if c1
!= c2 || c1
< 'a' || c1
> 'z' {
332 func lookup(tab
[]string, val
string) (int, string, error
) {
333 for i
, v
:= range tab
{
334 if len(val
) >= len(v
) && match(val
[0:len(v
)], v
) {
335 return i
, val
[len(v
):], nil
338 return -1, val
, errBad
341 // appendInt appends the decimal form of x to b and returns the result.
342 // If the decimal form (excluding sign) is shorter than width, the result is padded with leading 0's.
343 // Duplicates functionality in strconv, but avoids dependency.
344 func appendInt(b
[]byte, x
int, width
int) []byte {
351 // Assemble decimal in reverse order.
357 buf
[i
] = byte('0' + u
- q
*10)
361 buf
[i
] = byte('0' + u
)
364 for w
:= len(buf
) - i
; w
< width
; w
++ {
368 return append(b
, buf
[i
:]...)
371 // Never printed, just needs to be non-nil for return by atoi.
372 var atoiError
= errors
.New("time: invalid number")
374 // Duplicates functionality in strconv, but avoids dependency.
375 func atoi(s
string) (x
int, err error
) {
377 if s
!= "" && (s
[0] == '-' || s
[0] == '+') {
381 q
, rem
, err
:= leadingInt(s
)
383 if err
!= nil || rem
!= "" {
392 // formatNano appends a fractional second, as nanoseconds, to b
393 // and returns the result.
394 func formatNano(b
[]byte, nanosec
uint, n
int, trim
bool) []byte {
397 for start
:= len(buf
); start
> 0; {
399 buf
[start
] = byte(u%10
+ '0')
407 for n
> 0 && buf
[n
-1] == '0' {
415 return append(b
, buf
[:n
]...)
418 // String returns the time formatted using the format string
419 // "2006-01-02 15:04:05.999999999 -0700 MST"
420 func (t Time
) String() string {
421 return t
.Format("2006-01-02 15:04:05.999999999 -0700 MST")
424 // Format returns a textual representation of the time value formatted
425 // according to layout, which defines the format by showing how the reference
426 // time, defined to be
427 // Mon Jan 2 15:04:05 -0700 MST 2006
428 // would be displayed if it were the value; it serves as an example of the
429 // desired output. The same display rules will then be applied to the time
432 // A fractional second is represented by adding a period and zeros
433 // to the end of the seconds section of layout string, as in "15:04:05.000"
434 // to format a time stamp with millisecond precision.
436 // Predefined layouts ANSIC, UnixDate, RFC3339 and others describe standard
437 // and convenient representations of the reference time. For more information
438 // about the formats and the definition of the reference time, see the
439 // documentation for ANSIC and the other constants defined by this package.
440 func (t Time
) Format(layout
string) string {
443 max
:= len(layout
) + 10
445 var buf
[bufSize
]byte
448 b
= make([]byte, 0, max
)
450 b
= t
.AppendFormat(b
, layout
)
454 // AppendFormat is like Format but appends the textual
455 // representation to b and returns the extended buffer.
456 func (t Time
) AppendFormat(b
[]byte, layout
string) []byte {
458 name
, offset
, abs
= t
.locabs()
467 // Each iteration generates one std value.
469 prefix
, std
, suffix
:= nextStdChunk(layout
)
471 b
= append(b
, prefix
...)
478 // Compute year, month, day if needed.
479 if year
< 0 && std
&stdNeedDate
!= 0 {
480 year
, month
, day
, _
= absDate(abs
, true)
483 // Compute hour, minute, second if needed.
484 if hour
< 0 && std
&stdNeedClock
!= 0 {
485 hour
, min
, sec
= absClock(abs
)
488 switch std
& stdMask
{
494 b
= appendInt(b
, y%100
, 2)
496 b
= appendInt(b
, year
, 4)
498 b
= append(b
, month
.String()[:3]...)
503 b
= appendInt(b
, int(month
), 0)
505 b
= appendInt(b
, int(month
), 2)
507 b
= append(b
, absWeekday(abs
).String()[:3]...)
509 s
:= absWeekday(abs
).String()
512 b
= appendInt(b
, day
, 0)
517 b
= appendInt(b
, day
, 0)
519 b
= appendInt(b
, day
, 2)
521 b
= appendInt(b
, hour
, 2)
523 // Noon is 12PM, midnight is 12AM.
528 b
= appendInt(b
, hr
, 0)
530 // Noon is 12PM, midnight is 12AM.
535 b
= appendInt(b
, hr
, 2)
537 b
= appendInt(b
, min
, 0)
539 b
= appendInt(b
, min
, 2)
541 b
= appendInt(b
, sec
, 0)
543 b
= appendInt(b
, sec
, 2)
546 b
= append(b
, "PM"...)
548 b
= append(b
, "AM"...)
552 b
= append(b
, "pm"...)
554 b
= append(b
, "am"...)
556 case stdISO8601TZ
, stdISO8601ColonTZ
, stdISO8601SecondsTZ
, stdISO8601ShortTZ
, stdISO8601ColonSecondsTZ
, stdNumTZ
, stdNumColonTZ
, stdNumSecondsTz
, stdNumShortTZ
, stdNumColonSecondsTZ
:
557 // Ugly special case. We cheat and take the "Z" variants
558 // to mean "the time zone as formatted for ISO 8601".
559 if offset
== 0 && (std
== stdISO8601TZ || std
== stdISO8601ColonTZ || std
== stdISO8601SecondsTZ || std
== stdISO8601ShortTZ || std
== stdISO8601ColonSecondsTZ
) {
563 zone
:= offset
/ 60 // convert to minutes
568 absoffset
= -absoffset
572 b
= appendInt(b
, zone
/60, 2)
573 if std
== stdISO8601ColonTZ || std
== stdNumColonTZ || std
== stdISO8601ColonSecondsTZ || std
== stdNumColonSecondsTZ
{
576 if std
!= stdNumShortTZ
&& std
!= stdISO8601ShortTZ
{
577 b
= appendInt(b
, zone%60
, 2)
580 // append seconds if appropriate
581 if std
== stdISO8601SecondsTZ || std
== stdNumSecondsTz || std
== stdNumColonSecondsTZ || std
== stdISO8601ColonSecondsTZ
{
582 if std
== stdNumColonSecondsTZ || std
== stdISO8601ColonSecondsTZ
{
585 b
= appendInt(b
, absoffset%60
, 2)
590 b
= append(b
, name
...)
593 // No time zone known for this time, but we must print one.
594 // Use the -0700 format.
595 zone
:= offset
/ 60 // convert to minutes
602 b
= appendInt(b
, zone
/60, 2)
603 b
= appendInt(b
, zone%60
, 2)
604 case stdFracSecond0
, stdFracSecond9
:
605 b
= formatNano(b
, uint(t
.Nanosecond()), std
>>stdArgShift
, std
&stdMask
== stdFracSecond9
)
611 var errBad
= errors
.New("bad value for field") // placeholder not passed to user
613 // ParseError describes a problem parsing a time string.
614 type ParseError
struct {
622 func quote(s
string) string {
623 return "\"" + s
+ "\""
626 // Error returns the string representation of a ParseError.
627 func (e
*ParseError
) Error() string {
629 return "parsing time " +
630 quote(e
.Value
) + " as " +
631 quote(e
.Layout
) + ": cannot parse " +
632 quote(e
.ValueElem
) + " as " +
635 return "parsing time " +
636 quote(e
.Value
) + e
.Message
639 // isDigit reports whether s[i] is in range and is a decimal digit.
640 func isDigit(s
string, i
int) bool {
645 return '0' <= c
&& c
<= '9'
648 // getnum parses s[0:1] or s[0:2] (fixed forces the latter)
649 // as a decimal integer and returns the integer and the
650 // remainder of the string.
651 func getnum(s
string, fixed
bool) (int, string, error
) {
659 return int(s
[0] - '0'), s
[1:], nil
661 return int(s
[0]-'0')*10 + int(s
[1]-'0'), s
[2:], nil
664 func cutspace(s
string) string {
665 for len(s
) > 0 && s
[0] == ' ' {
671 // skip removes the given prefix from value,
672 // treating runs of space characters as equivalent.
673 func skip(value
, prefix
string) (string, error
) {
674 for len(prefix
) > 0 {
675 if prefix
[0] == ' ' {
676 if len(value
) > 0 && value
[0] != ' ' {
679 prefix
= cutspace(prefix
)
680 value
= cutspace(value
)
683 if len(value
) == 0 || value
[0] != prefix
[0] {
692 // Parse parses a formatted string and returns the time value it represents.
693 // The layout defines the format by showing how the reference time,
695 // Mon Jan 2 15:04:05 -0700 MST 2006
696 // would be interpreted if it were the value; it serves as an example of
697 // the input format. The same interpretation will then be made to the
700 // Predefined layouts ANSIC, UnixDate, RFC3339 and others describe standard
701 // and convenient representations of the reference time. For more information
702 // about the formats and the definition of the reference time, see the
703 // documentation for ANSIC and the other constants defined by this package.
704 // Also, the executable example for time.Format demonstrates the working
705 // of the layout string in detail and is a good reference.
707 // Elements omitted from the value are assumed to be zero or, when
708 // zero is impossible, one, so parsing "3:04pm" returns the time
709 // corresponding to Jan 1, year 0, 15:04:00 UTC (note that because the year is
710 // 0, this time is before the zero Time).
711 // Years must be in the range 0000..9999. The day of the week is checked
712 // for syntax but it is otherwise ignored.
714 // In the absence of a time zone indicator, Parse returns a time in UTC.
716 // When parsing a time with a zone offset like -0700, if the offset corresponds
717 // to a time zone used by the current location (Local), then Parse uses that
718 // location and zone in the returned time. Otherwise it records the time as
719 // being in a fabricated location with time fixed at the given zone offset.
721 // No checking is done that the day of the month is within the month's
722 // valid dates; any one- or two-digit value is accepted. For example
723 // February 31 and even February 99 are valid dates, specifying dates
724 // in March and May. This behavior is consistent with time.Date.
726 // When parsing a time with a zone abbreviation like MST, if the zone abbreviation
727 // has a defined offset in the current location, then that offset is used.
728 // The zone abbreviation "UTC" is recognized as UTC regardless of location.
729 // If the zone abbreviation is unknown, Parse records the time as being
730 // in a fabricated location with the given zone abbreviation and a zero offset.
731 // This choice means that such a time can be parsed and reformatted with the
732 // same layout losslessly, but the exact instant used in the representation will
733 // differ by the actual zone offset. To avoid such problems, prefer time layouts
734 // that use a numeric zone offset, or use ParseInLocation.
735 func Parse(layout
, value
string) (Time
, error
) {
736 return parse(layout
, value
, UTC
, Local
)
739 // ParseInLocation is like Parse but differs in two important ways.
740 // First, in the absence of time zone information, Parse interprets a time as UTC;
741 // ParseInLocation interprets the time as in the given location.
742 // Second, when given a zone offset or abbreviation, Parse tries to match it
743 // against the Local location; ParseInLocation uses the given location.
744 func ParseInLocation(layout
, value
string, loc
*Location
) (Time
, error
) {
745 return parse(layout
, value
, loc
, loc
)
748 func parse(layout
, value
string, defaultLocation
, local
*Location
) (Time
, error
) {
749 alayout
, avalue
:= layout
, value
750 rangeErrString
:= "" // set if a value is out of range
751 amSet
:= false // do we need to subtract 12 from the hour for midnight?
752 pmSet
:= false // do we need to add 12 to the hour?
754 // Time being constructed.
757 month
int = 1 // January
768 // Each iteration processes one std value.
771 prefix
, std
, suffix
:= nextStdChunk(layout
)
772 stdstr
:= layout
[len(prefix
) : len(layout
)-len(suffix
)]
773 value
, err
= skip(value
, prefix
)
775 return Time
{}, &ParseError
{alayout
, avalue
, prefix
, value
, ""}
779 return Time
{}, &ParseError
{alayout
, avalue
, "", value
, ": extra text: " + value
}
785 switch std
& stdMask
{
791 p
, value
= value
[0:2], value
[2:]
793 if year
>= 69 { // Unix time starts Dec 31 1969 in some time zones
799 if len(value
) < 4 ||
!isDigit(value
, 0) {
803 p
, value
= value
[0:4], value
[4:]
806 month
, value
, err
= lookup(shortMonthNames
, value
)
808 month
, value
, err
= lookup(longMonthNames
, value
)
809 case stdNumMonth
, stdZeroMonth
:
810 month
, value
, err
= getnum(value
, std
== stdZeroMonth
)
811 if month
<= 0 ||
12 < month
{
812 rangeErrString
= "month"
815 // Ignore weekday except for error checking.
816 _
, value
, err
= lookup(shortDayNames
, value
)
818 _
, value
, err
= lookup(longDayNames
, value
)
819 case stdDay
, stdUnderDay
, stdZeroDay
:
820 if std
== stdUnderDay
&& len(value
) > 0 && value
[0] == ' ' {
823 day
, value
, err
= getnum(value
, std
== stdZeroDay
)
825 // Note that we allow any one- or two-digit day here.
826 rangeErrString
= "day"
829 hour
, value
, err
= getnum(value
, false)
830 if hour
< 0 ||
24 <= hour
{
831 rangeErrString
= "hour"
833 case stdHour12
, stdZeroHour12
:
834 hour
, value
, err
= getnum(value
, std
== stdZeroHour12
)
835 if hour
< 0 ||
12 < hour
{
836 rangeErrString
= "hour"
838 case stdMinute
, stdZeroMinute
:
839 min
, value
, err
= getnum(value
, std
== stdZeroMinute
)
840 if min
< 0 ||
60 <= min
{
841 rangeErrString
= "minute"
843 case stdSecond
, stdZeroSecond
:
844 sec
, value
, err
= getnum(value
, std
== stdZeroSecond
)
845 if sec
< 0 ||
60 <= sec
{
846 rangeErrString
= "second"
848 // Special case: do we have a fractional second but no
849 // fractional second in the format?
850 if len(value
) >= 2 && value
[0] == '.' && isDigit(value
, 1) {
851 _
, std
, _
= nextStdChunk(layout
)
853 if std
== stdFracSecond0 || std
== stdFracSecond9
{
854 // Fractional second in the layout; proceed normally
857 // No fractional second in the layout but we have one in the input.
859 for ; n
< len(value
) && isDigit(value
, n
); n
++ {
861 nsec
, rangeErrString
, err
= parseNanoseconds(value
, n
)
869 p
, value
= value
[0:2], value
[2:]
883 p
, value
= value
[0:2], value
[2:]
892 case stdISO8601TZ
, stdISO8601ColonTZ
, stdISO8601SecondsTZ
, stdISO8601ShortTZ
, stdISO8601ColonSecondsTZ
, stdNumTZ
, stdNumShortTZ
, stdNumColonTZ
, stdNumSecondsTz
, stdNumColonSecondsTZ
:
893 if (std
== stdISO8601TZ || std
== stdISO8601ShortTZ || std
== stdISO8601ColonTZ
) && len(value
) >= 1 && value
[0] == 'Z' {
898 var sign
, hour
, min
, seconds
string
899 if std
== stdISO8601ColonTZ || std
== stdNumColonTZ
{
908 sign
, hour
, min
, seconds
, value
= value
[0:1], value
[1:3], value
[4:6], "00", value
[6:]
909 } else if std
== stdNumShortTZ || std
== stdISO8601ShortTZ
{
914 sign
, hour
, min
, seconds
, value
= value
[0:1], value
[1:3], "00", "00", value
[3:]
915 } else if std
== stdISO8601ColonSecondsTZ || std
== stdNumColonSecondsTZ
{
920 if value
[3] != ':' || value
[6] != ':' {
924 sign
, hour
, min
, seconds
, value
= value
[0:1], value
[1:3], value
[4:6], value
[7:9], value
[9:]
925 } else if std
== stdISO8601SecondsTZ || std
== stdNumSecondsTz
{
930 sign
, hour
, min
, seconds
, value
= value
[0:1], value
[1:3], value
[3:5], value
[5:7], value
[7:]
936 sign
, hour
, min
, seconds
, value
= value
[0:1], value
[1:3], value
[3:5], "00", value
[5:]
944 ss
, err
= atoi(seconds
)
946 zoneOffset
= (hr
*60+mm
)*60 + ss
// offset is in seconds
950 zoneOffset
= -zoneOffset
955 // Does it look like a time zone?
956 if len(value
) >= 3 && value
[0:3] == "UTC" {
961 n
, ok
:= parseTimeZone(value
)
966 zoneName
, value
= value
[:n
], value
[n
:]
969 // stdFracSecond0 requires the exact number of digits as specified in
971 ndigit
:= 1 + (std
>> stdArgShift
)
972 if len(value
) < ndigit
{
976 nsec
, rangeErrString
, err
= parseNanoseconds(value
, ndigit
)
977 value
= value
[ndigit
:]
980 if len(value
) < 2 || value
[0] != '.' || value
[1] < '0' ||
'9' < value
[1] {
981 // Fractional second omitted.
984 // Take any number of digits, even more than asked for,
985 // because it is what the stdSecond case would do.
987 for i
< 9 && i
+1 < len(value
) && '0' <= value
[i
+1] && value
[i
+1] <= '9' {
990 nsec
, rangeErrString
, err
= parseNanoseconds(value
, 1+i
)
993 if rangeErrString
!= "" {
994 return Time
{}, &ParseError
{alayout
, avalue
, stdstr
, value
, ": " + rangeErrString
+ " out of range"}
997 return Time
{}, &ParseError
{alayout
, avalue
, stdstr
, value
, ""}
1000 if pmSet
&& hour
< 12 {
1002 } else if amSet
&& hour
== 12 {
1006 // Validate the day of the month.
1007 if day
> daysIn(Month(month
), year
) {
1008 return Time
{}, &ParseError
{alayout
, avalue
, "", value
, ": day out of range"}
1012 return Date(year
, Month(month
), day
, hour
, min
, sec
, nsec
, z
), nil
1015 if zoneOffset
!= -1 {
1016 t
:= Date(year
, Month(month
), day
, hour
, min
, sec
, nsec
, UTC
)
1017 t
.sec
-= int64(zoneOffset
)
1019 // Look for local zone with the given offset.
1020 // If that zone was in effect at the given time, use it.
1021 name
, offset
, _
, _
, _
:= local
.lookup(t
.sec
+ internalToUnix
)
1022 if offset
== zoneOffset
&& (zoneName
== "" || name
== zoneName
) {
1027 // Otherwise create fake zone to record offset.
1028 t
.loc
= FixedZone(zoneName
, zoneOffset
)
1033 t
:= Date(year
, Month(month
), day
, hour
, min
, sec
, nsec
, UTC
)
1034 // Look for local zone with the given offset.
1035 // If that zone was in effect at the given time, use it.
1036 offset
, _
, ok
:= local
.lookupName(zoneName
, t
.sec
+internalToUnix
)
1038 t
.sec
-= int64(offset
)
1043 // Otherwise, create fake zone with unknown offset.
1044 if len(zoneName
) > 3 && zoneName
[:3] == "GMT" {
1045 offset
, _
= atoi(zoneName
[3:]) // Guaranteed OK by parseGMT.
1048 t
.loc
= FixedZone(zoneName
, offset
)
1052 // Otherwise, fall back to default.
1053 return Date(year
, Month(month
), day
, hour
, min
, sec
, nsec
, defaultLocation
), nil
1056 // parseTimeZone parses a time zone string and returns its length. Time zones
1057 // are human-generated and unpredictable. We can't do precise error checking.
1058 // On the other hand, for a correct parse there must be a time zone at the
1059 // beginning of the string, so it's almost always true that there's one
1060 // there. We look at the beginning of the string for a run of upper-case letters.
1061 // If there are more than 5, it's an error.
1062 // If there are 4 or 5 and the last is a T, it's a time zone.
1063 // If there are 3, it's a time zone.
1064 // Otherwise, other than special cases, it's not a time zone.
1065 // GMT is special because it can have an hour offset.
1066 func parseTimeZone(value
string) (length
int, ok
bool) {
1070 // Special case 1: ChST and MeST are the only zones with a lower-case letter.
1071 if len(value
) >= 4 && (value
[:4] == "ChST" || value
[:4] == "MeST") {
1074 // Special case 2: GMT may have an hour offset; treat it specially.
1075 if value
[:3] == "GMT" {
1076 length
= parseGMT(value
)
1079 // How many upper-case letters are there? Need at least three, at most five.
1081 for nUpper
= 0; nUpper
< 6; nUpper
++ {
1082 if nUpper
>= len(value
) {
1085 if c
:= value
[nUpper
]; c
< 'A' ||
'Z' < c
{
1092 case 5: // Must end in T to match.
1093 if value
[4] == 'T' {
1096 case 4: // Must end in T to match.
1097 if value
[3] == 'T' {
1106 // parseGMT parses a GMT time zone. The input string is known to start "GMT".
1107 // The function checks whether that is followed by a sign and a number in the
1108 // range -14 through 12 excluding zero.
1109 func parseGMT(value
string) int {
1111 if len(value
) == 0 {
1115 if sign
!= '-' && sign
!= '+' {
1118 x
, rem
, err
:= leadingInt(value
[1:])
1125 if x
== 0 || x
< -14 ||
12 < x
{
1128 return 3 + len(value
) - len(rem
)
1131 func parseNanoseconds(value
string, nbytes
int) (ns
int, rangeErrString
string, err error
) {
1132 if value
[0] != '.' {
1136 if ns
, err
= atoi(value
[1:nbytes
]); err
!= nil {
1139 if ns
< 0 ||
1e9
<= ns
{
1140 rangeErrString
= "fractional second"
1143 // We need nanoseconds, which means scaling by the number
1144 // of missing digits in the format, maximum length 10. If it's
1145 // longer than 10, we won't scale.
1146 scaleDigits
:= 10 - nbytes
1147 for i
:= 0; i
< scaleDigits
; i
++ {
1153 var errLeadingInt
= errors
.New("time: bad [0-9]*") // never printed
1155 // leadingInt consumes the leading [0-9]* from s.
1156 func leadingInt(s
string) (x
int64, rem
string, err error
) {
1158 for ; i
< len(s
); i
++ {
1160 if c
< '0' || c
> '9' {
1163 if x
> (1<<63-1)/10 {
1165 return 0, "", errLeadingInt
1167 x
= x
*10 + int64(c
) - '0'
1170 return 0, "", errLeadingInt
1173 return x
, s
[i
:], nil
1176 var unitMap
= map[string]int64{
1177 "ns": int64(Nanosecond
),
1178 "us": int64(Microsecond
),
1179 "µs": int64(Microsecond
), // U+00B5 = micro symbol
1180 "μs": int64(Microsecond
), // U+03BC = Greek letter mu
1181 "ms": int64(Millisecond
),
1187 // ParseDuration parses a duration string.
1188 // A duration string is a possibly signed sequence of
1189 // decimal numbers, each with optional fraction and a unit suffix,
1190 // such as "300ms", "-1.5h" or "2h45m".
1191 // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
1192 func ParseDuration(s
string) (Duration
, error
) {
1193 // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
1201 if c
== '-' || c
== '+' {
1206 // Special case: if all that is left is "0", this is zero.
1211 return 0, errors
.New("time: invalid duration " + orig
)
1215 v
, f
int64 // integers before, after decimal point
1216 scale
float64 = 1 // value = v + f/scale
1221 // The next character must be [0-9.]
1222 if !(s
[0] == '.' ||
'0' <= s
[0] && s
[0] <= '9') {
1223 return 0, errors
.New("time: invalid duration " + orig
)
1227 v
, s
, err
= leadingInt(s
)
1229 return 0, errors
.New("time: invalid duration " + orig
)
1231 pre
:= pl
!= len(s
) // whether we consumed anything before a period
1233 // Consume (\.[0-9]*)?
1235 if s
!= "" && s
[0] == '.' {
1238 f
, s
, err
= leadingInt(s
)
1240 return 0, errors
.New("time: invalid duration " + orig
)
1242 for n
:= pl
- len(s
); n
> 0; n
-- {
1248 // no digits (e.g. ".s" or "-.s")
1249 return 0, errors
.New("time: invalid duration " + orig
)
1254 for ; i
< len(s
); i
++ {
1256 if c
== '.' ||
'0' <= c
&& c
<= '9' {
1261 return 0, errors
.New("time: missing unit in duration " + orig
)
1265 unit
, ok
:= unitMap
[u
]
1267 return 0, errors
.New("time: unknown unit " + u
+ " in duration " + orig
)
1269 if v
> (1<<63-1)/unit
{
1271 return 0, errors
.New("time: invalid duration " + orig
)
1275 // float64 is needed to be nanosecond accurate for fractions of hours.
1276 // v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit)
1277 v
+= int64(float64(f
) * (float64(unit
) / scale
))
1280 return 0, errors
.New("time: invalid duration " + orig
)
1286 return 0, errors
.New("time: invalid duration " + orig
)
1293 return Duration(d
), nil