Source file
src/time/format.go
1
2
3
4
5 package time
6
7 import (
8 "errors"
9 "internal/stringslite"
10 _ "unsafe"
11 )
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 const (
110 Layout = "01/02 03:04:05PM '06 -0700"
111 ANSIC = "Mon Jan _2 15:04:05 2006"
112 UnixDate = "Mon Jan _2 15:04:05 MST 2006"
113 RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
114 RFC822 = "02 Jan 06 15:04 MST"
115 RFC822Z = "02 Jan 06 15:04 -0700"
116 RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
117 RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
118 RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700"
119 RFC3339 = "2006-01-02T15:04:05Z07:00"
120 RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
121 Kitchen = "3:04PM"
122
123 Stamp = "Jan _2 15:04:05"
124 StampMilli = "Jan _2 15:04:05.000"
125 StampMicro = "Jan _2 15:04:05.000000"
126 StampNano = "Jan _2 15:04:05.000000000"
127 DateTime = "2006-01-02 15:04:05"
128 DateOnly = "2006-01-02"
129 TimeOnly = "15:04:05"
130 )
131
132 const (
133 _ = iota
134 stdLongMonth = iota + stdNeedDate
135 stdMonth
136 stdNumMonth
137 stdZeroMonth
138 stdLongWeekDay
139 stdWeekDay
140 stdDay
141 stdUnderDay
142 stdZeroDay
143 stdUnderYearDay = iota + stdNeedYday
144 stdZeroYearDay
145 stdHour = iota + stdNeedClock
146 stdHour12
147 stdZeroHour12
148 stdMinute
149 stdZeroMinute
150 stdSecond
151 stdZeroSecond
152 stdLongYear = iota + stdNeedDate
153 stdYear
154 stdPM = iota + stdNeedClock
155 stdpm
156 stdTZ = iota
157 stdISO8601TZ
158 stdISO8601SecondsTZ
159 stdISO8601ShortTZ
160 stdISO8601ColonTZ
161 stdISO8601ColonSecondsTZ
162 stdNumTZ
163 stdNumSecondsTz
164 stdNumShortTZ
165 stdNumColonTZ
166 stdNumColonSecondsTZ
167 stdFracSecond0
168 stdFracSecond9
169
170 stdNeedDate = 1 << 8
171 stdNeedYday = 1 << 9
172 stdNeedClock = 1 << 10
173 stdArgShift = 16
174 stdSeparatorShift = 28
175 stdMask = 1<<stdArgShift - 1
176 )
177
178
179 var std0x = [...]int{stdZeroMonth, stdZeroDay, stdZeroHour12, stdZeroMinute, stdZeroSecond, stdYear}
180
181
182
183 func startsWithLowerCase(str string) bool {
184 if len(str) == 0 {
185 return false
186 }
187 c := str[0]
188 return 'a' <= c && c <= 'z'
189 }
190
191
192
193
194
195
196
197
198
199
200
201
202
203 func nextStdChunk(layout string) (prefix string, std int, suffix string) {
204 for i := 0; i < len(layout); i++ {
205 switch c := int(layout[i]); c {
206 case 'J':
207 if len(layout) >= i+3 && layout[i:i+3] == "Jan" {
208 if len(layout) >= i+7 && layout[i:i+7] == "January" {
209 return layout[0:i], stdLongMonth, layout[i+7:]
210 }
211 if !startsWithLowerCase(layout[i+3:]) {
212 return layout[0:i], stdMonth, layout[i+3:]
213 }
214 }
215
216 case 'M':
217 if len(layout) >= i+3 {
218 if layout[i:i+3] == "Mon" {
219 if len(layout) >= i+6 && layout[i:i+6] == "Monday" {
220 return layout[0:i], stdLongWeekDay, layout[i+6:]
221 }
222 if !startsWithLowerCase(layout[i+3:]) {
223 return layout[0:i], stdWeekDay, layout[i+3:]
224 }
225 }
226 if layout[i:i+3] == "MST" {
227 return layout[0:i], stdTZ, layout[i+3:]
228 }
229 }
230
231 case '0':
232 if len(layout) >= i+2 && '1' <= layout[i+1] && layout[i+1] <= '6' {
233 return layout[0:i], std0x[layout[i+1]-'1'], layout[i+2:]
234 }
235 if len(layout) >= i+3 && layout[i+1] == '0' && layout[i+2] == '2' {
236 return layout[0:i], stdZeroYearDay, layout[i+3:]
237 }
238
239 case '1':
240 if len(layout) >= i+2 && layout[i+1] == '5' {
241 return layout[0:i], stdHour, layout[i+2:]
242 }
243 return layout[0:i], stdNumMonth, layout[i+1:]
244
245 case '2':
246 if len(layout) >= i+4 && layout[i:i+4] == "2006" {
247 return layout[0:i], stdLongYear, layout[i+4:]
248 }
249 return layout[0:i], stdDay, layout[i+1:]
250
251 case '_':
252 if len(layout) >= i+2 && layout[i+1] == '2' {
253
254 if len(layout) >= i+5 && layout[i+1:i+5] == "2006" {
255 return layout[0 : i+1], stdLongYear, layout[i+5:]
256 }
257 return layout[0:i], stdUnderDay, layout[i+2:]
258 }
259 if len(layout) >= i+3 && layout[i+1] == '_' && layout[i+2] == '2' {
260 return layout[0:i], stdUnderYearDay, layout[i+3:]
261 }
262
263 case '3':
264 return layout[0:i], stdHour12, layout[i+1:]
265
266 case '4':
267 return layout[0:i], stdMinute, layout[i+1:]
268
269 case '5':
270 return layout[0:i], stdSecond, layout[i+1:]
271
272 case 'P':
273 if len(layout) >= i+2 && layout[i+1] == 'M' {
274 return layout[0:i], stdPM, layout[i+2:]
275 }
276
277 case 'p':
278 if len(layout) >= i+2 && layout[i+1] == 'm' {
279 return layout[0:i], stdpm, layout[i+2:]
280 }
281
282 case '-':
283 if len(layout) >= i+7 && layout[i:i+7] == "-070000" {
284 return layout[0:i], stdNumSecondsTz, layout[i+7:]
285 }
286 if len(layout) >= i+9 && layout[i:i+9] == "-07:00:00" {
287 return layout[0:i], stdNumColonSecondsTZ, layout[i+9:]
288 }
289 if len(layout) >= i+5 && layout[i:i+5] == "-0700" {
290 return layout[0:i], stdNumTZ, layout[i+5:]
291 }
292 if len(layout) >= i+6 && layout[i:i+6] == "-07:00" {
293 return layout[0:i], stdNumColonTZ, layout[i+6:]
294 }
295 if len(layout) >= i+3 && layout[i:i+3] == "-07" {
296 return layout[0:i], stdNumShortTZ, layout[i+3:]
297 }
298
299 case 'Z':
300 if len(layout) >= i+7 && layout[i:i+7] == "Z070000" {
301 return layout[0:i], stdISO8601SecondsTZ, layout[i+7:]
302 }
303 if len(layout) >= i+9 && layout[i:i+9] == "Z07:00:00" {
304 return layout[0:i], stdISO8601ColonSecondsTZ, layout[i+9:]
305 }
306 if len(layout) >= i+5 && layout[i:i+5] == "Z0700" {
307 return layout[0:i], stdISO8601TZ, layout[i+5:]
308 }
309 if len(layout) >= i+6 && layout[i:i+6] == "Z07:00" {
310 return layout[0:i], stdISO8601ColonTZ, layout[i+6:]
311 }
312 if len(layout) >= i+3 && layout[i:i+3] == "Z07" {
313 return layout[0:i], stdISO8601ShortTZ, layout[i+3:]
314 }
315
316 case '.', ',':
317 if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') {
318 ch := layout[i+1]
319 j := i + 1
320 for j < len(layout) && layout[j] == ch {
321 j++
322 }
323
324 if !isDigit(layout, j) {
325 code := stdFracSecond0
326 if layout[i+1] == '9' {
327 code = stdFracSecond9
328 }
329 std := stdFracSecond(code, j-(i+1), c)
330 return layout[0:i], std, layout[j:]
331 }
332 }
333 }
334 }
335 return layout, 0, ""
336 }
337
338 var longDayNames = []string{
339 "Sunday",
340 "Monday",
341 "Tuesday",
342 "Wednesday",
343 "Thursday",
344 "Friday",
345 "Saturday",
346 }
347
348 var shortDayNames = []string{
349 "Sun",
350 "Mon",
351 "Tue",
352 "Wed",
353 "Thu",
354 "Fri",
355 "Sat",
356 }
357
358 var shortMonthNames = []string{
359 "Jan",
360 "Feb",
361 "Mar",
362 "Apr",
363 "May",
364 "Jun",
365 "Jul",
366 "Aug",
367 "Sep",
368 "Oct",
369 "Nov",
370 "Dec",
371 }
372
373 var longMonthNames = []string{
374 "January",
375 "February",
376 "March",
377 "April",
378 "May",
379 "June",
380 "July",
381 "August",
382 "September",
383 "October",
384 "November",
385 "December",
386 }
387
388
389
390 func match(s1, s2 string) bool {
391 for i := 0; i < len(s1); i++ {
392 c1 := s1[i]
393 c2 := s2[i]
394 if c1 != c2 {
395
396 c1 |= 'a' - 'A'
397 c2 |= 'a' - 'A'
398 if c1 != c2 || c1 < 'a' || c1 > 'z' {
399 return false
400 }
401 }
402 }
403 return true
404 }
405
406 func lookup(tab []string, val string) (int, string, error) {
407 for i, v := range tab {
408 if len(val) >= len(v) && match(val[:len(v)], v) {
409 return i, val[len(v):], nil
410 }
411 }
412 return -1, val, errBad
413 }
414
415
416
417
418 func appendInt(b []byte, x int, width int) []byte {
419 u := uint(x)
420 if x < 0 {
421 b = append(b, '-')
422 u = uint(-x)
423 }
424
425
426 utod := func(u uint) byte { return '0' + byte(u) }
427 switch {
428 case width == 2 && u < 1e2:
429 return append(b, utod(u/1e1), utod(u%1e1))
430 case width == 4 && u < 1e4:
431 return append(b, utod(u/1e3), utod(u/1e2%1e1), utod(u/1e1%1e1), utod(u%1e1))
432 }
433
434
435 var n int
436 if u == 0 {
437 n = 1
438 }
439 for u2 := u; u2 > 0; u2 /= 10 {
440 n++
441 }
442
443
444 for pad := width - n; pad > 0; pad-- {
445 b = append(b, '0')
446 }
447
448
449 if len(b)+n <= cap(b) {
450 b = b[:len(b)+n]
451 } else {
452 b = append(b, make([]byte, n)...)
453 }
454
455
456 i := len(b) - 1
457 for u >= 10 && i > 0 {
458 q := u / 10
459 b[i] = utod(u - q*10)
460 u = q
461 i--
462 }
463 b[i] = utod(u)
464 return b
465 }
466
467
468 var errAtoi = errors.New("time: invalid number")
469
470
471 func atoi[bytes []byte | string](s bytes) (x int, err error) {
472 neg := false
473 if len(s) > 0 && (s[0] == '-' || s[0] == '+') {
474 neg = s[0] == '-'
475 s = s[1:]
476 }
477 q, rem, err := leadingInt(s)
478 x = int(q)
479 if err != nil || len(rem) > 0 {
480 return 0, errAtoi
481 }
482 if neg {
483 x = -x
484 }
485 return x, nil
486 }
487
488
489
490
491 func stdFracSecond(code, n, c int) int {
492
493 if c == '.' {
494 return code | ((n & 0xfff) << stdArgShift)
495 }
496 return code | ((n & 0xfff) << stdArgShift) | 1<<stdSeparatorShift
497 }
498
499 func digitsLen(std int) int {
500 return (std >> stdArgShift) & 0xfff
501 }
502
503 func separator(std int) byte {
504 if (std >> stdSeparatorShift) == 0 {
505 return '.'
506 }
507 return ','
508 }
509
510
511
512 func appendNano(b []byte, nanosec int, std int) []byte {
513 trim := std&stdMask == stdFracSecond9
514 n := digitsLen(std)
515 if trim && (n == 0 || nanosec == 0) {
516 return b
517 }
518 dot := separator(std)
519 b = append(b, dot)
520 b = appendInt(b, nanosec, 9)
521 if n < 9 {
522 b = b[:len(b)-9+n]
523 }
524 if trim {
525 for len(b) > 0 && b[len(b)-1] == '0' {
526 b = b[:len(b)-1]
527 }
528 if len(b) > 0 && b[len(b)-1] == dot {
529 b = b[:len(b)-1]
530 }
531 }
532 return b
533 }
534
535
536
537
538
539
540
541
542
543
544
545
546 func (t Time) String() string {
547 s := t.Format("2006-01-02 15:04:05.999999999 -0700 MST")
548
549
550 if t.wall&hasMonotonic != 0 {
551 m2 := uint64(t.ext)
552 sign := byte('+')
553 if t.ext < 0 {
554 sign = '-'
555 m2 = -m2
556 }
557 m1, m2 := m2/1e9, m2%1e9
558 m0, m1 := m1/1e9, m1%1e9
559 buf := make([]byte, 0, 24)
560 buf = append(buf, " m="...)
561 buf = append(buf, sign)
562 wid := 0
563 if m0 != 0 {
564 buf = appendInt(buf, int(m0), 0)
565 wid = 9
566 }
567 buf = appendInt(buf, int(m1), wid)
568 buf = append(buf, '.')
569 buf = appendInt(buf, int(m2), 9)
570 s += string(buf)
571 }
572 return s
573 }
574
575
576
577 func (t Time) GoString() string {
578 abs := t.absSec()
579 year, month, day := abs.days().date()
580 hour, minute, second := abs.clock()
581
582 buf := make([]byte, 0, len("time.Date(9999, time.September, 31, 23, 59, 59, 999999999, time.Local)"))
583 buf = append(buf, "time.Date("...)
584 buf = appendInt(buf, year, 0)
585 if January <= month && month <= December {
586 buf = append(buf, ", time."...)
587 buf = append(buf, longMonthNames[month-1]...)
588 } else {
589
590
591 buf = appendInt(buf, int(month), 0)
592 }
593 buf = append(buf, ", "...)
594 buf = appendInt(buf, day, 0)
595 buf = append(buf, ", "...)
596 buf = appendInt(buf, hour, 0)
597 buf = append(buf, ", "...)
598 buf = appendInt(buf, minute, 0)
599 buf = append(buf, ", "...)
600 buf = appendInt(buf, second, 0)
601 buf = append(buf, ", "...)
602 buf = appendInt(buf, t.Nanosecond(), 0)
603 buf = append(buf, ", "...)
604 switch loc := t.Location(); loc {
605 case UTC, nil:
606 buf = append(buf, "time.UTC"...)
607 case Local:
608 buf = append(buf, "time.Local"...)
609 default:
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625 buf = append(buf, `time.Location(`...)
626 buf = append(buf, quote(loc.name)...)
627 buf = append(buf, ')')
628 }
629 buf = append(buf, ')')
630 return string(buf)
631 }
632
633
634
635
636
637
638
639 func (t Time) Format(layout string) string {
640 const bufSize = 64
641 var b []byte
642 max := len(layout) + 10
643 if max < bufSize {
644 var buf [bufSize]byte
645 b = buf[:0]
646 } else {
647 b = make([]byte, 0, max)
648 }
649 b = t.AppendFormat(b, layout)
650 return string(b)
651 }
652
653
654
655 func (t Time) AppendFormat(b []byte, layout string) []byte {
656
657 switch layout {
658 case RFC3339:
659 return t.appendFormatRFC3339(b, false)
660 case RFC3339Nano:
661 return t.appendFormatRFC3339(b, true)
662 default:
663 return t.appendFormat(b, layout)
664 }
665 }
666
667 func (t Time) appendFormat(b []byte, layout string) []byte {
668 name, offset, abs := t.locabs()
669 days := abs.days()
670
671 var (
672 year int = -1
673 month Month
674 day int
675 yday int = -1
676 hour int = -1
677 min int
678 sec int
679 )
680
681
682 for layout != "" {
683 prefix, std, suffix := nextStdChunk(layout)
684 if prefix != "" {
685 b = append(b, prefix...)
686 }
687 if std == 0 {
688 break
689 }
690 layout = suffix
691
692
693 if year < 0 && std&stdNeedDate != 0 {
694 year, month, day = days.date()
695 }
696 if yday < 0 && std&stdNeedYday != 0 {
697 _, yday = days.yearYday()
698 }
699
700
701 if hour < 0 && std&stdNeedClock != 0 {
702 hour, min, sec = abs.clock()
703 }
704
705 switch std & stdMask {
706 case stdYear:
707 y := year
708 if y < 0 {
709 y = -y
710 }
711 b = appendInt(b, y%100, 2)
712 case stdLongYear:
713 b = appendInt(b, year, 4)
714 case stdMonth:
715 b = append(b, month.String()[:3]...)
716 case stdLongMonth:
717 m := month.String()
718 b = append(b, m...)
719 case stdNumMonth:
720 b = appendInt(b, int(month), 0)
721 case stdZeroMonth:
722 b = appendInt(b, int(month), 2)
723 case stdWeekDay:
724 b = append(b, days.weekday().String()[:3]...)
725 case stdLongWeekDay:
726 s := days.weekday().String()
727 b = append(b, s...)
728 case stdDay:
729 b = appendInt(b, day, 0)
730 case stdUnderDay:
731 if day < 10 {
732 b = append(b, ' ')
733 }
734 b = appendInt(b, day, 0)
735 case stdZeroDay:
736 b = appendInt(b, day, 2)
737 case stdUnderYearDay:
738 if yday < 100 {
739 b = append(b, ' ')
740 if yday < 10 {
741 b = append(b, ' ')
742 }
743 }
744 b = appendInt(b, yday, 0)
745 case stdZeroYearDay:
746 b = appendInt(b, yday, 3)
747 case stdHour:
748 b = appendInt(b, hour, 2)
749 case stdHour12:
750
751 hr := hour % 12
752 if hr == 0 {
753 hr = 12
754 }
755 b = appendInt(b, hr, 0)
756 case stdZeroHour12:
757
758 hr := hour % 12
759 if hr == 0 {
760 hr = 12
761 }
762 b = appendInt(b, hr, 2)
763 case stdMinute:
764 b = appendInt(b, min, 0)
765 case stdZeroMinute:
766 b = appendInt(b, min, 2)
767 case stdSecond:
768 b = appendInt(b, sec, 0)
769 case stdZeroSecond:
770 b = appendInt(b, sec, 2)
771 case stdPM:
772 if hour >= 12 {
773 b = append(b, "PM"...)
774 } else {
775 b = append(b, "AM"...)
776 }
777 case stdpm:
778 if hour >= 12 {
779 b = append(b, "pm"...)
780 } else {
781 b = append(b, "am"...)
782 }
783 case stdISO8601TZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ShortTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumColonTZ, stdNumSecondsTz, stdNumShortTZ, stdNumColonSecondsTZ:
784
785
786 if offset == 0 && (std == stdISO8601TZ || std == stdISO8601ColonTZ || std == stdISO8601SecondsTZ || std == stdISO8601ShortTZ || std == stdISO8601ColonSecondsTZ) {
787 b = append(b, 'Z')
788 break
789 }
790 zone := offset / 60
791 absoffset := offset
792 if zone < 0 {
793 b = append(b, '-')
794 zone = -zone
795 absoffset = -absoffset
796 } else {
797 b = append(b, '+')
798 }
799 b = appendInt(b, zone/60, 2)
800 if std == stdISO8601ColonTZ || std == stdNumColonTZ || std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ {
801 b = append(b, ':')
802 }
803 if std != stdNumShortTZ && std != stdISO8601ShortTZ {
804 b = appendInt(b, zone%60, 2)
805 }
806
807
808 if std == stdISO8601SecondsTZ || std == stdNumSecondsTz || std == stdNumColonSecondsTZ || std == stdISO8601ColonSecondsTZ {
809 if std == stdNumColonSecondsTZ || std == stdISO8601ColonSecondsTZ {
810 b = append(b, ':')
811 }
812 b = appendInt(b, absoffset%60, 2)
813 }
814
815 case stdTZ:
816 if name != "" {
817 b = append(b, name...)
818 break
819 }
820
821
822 zone := offset / 60
823 if zone < 0 {
824 b = append(b, '-')
825 zone = -zone
826 } else {
827 b = append(b, '+')
828 }
829 b = appendInt(b, zone/60, 2)
830 b = appendInt(b, zone%60, 2)
831 case stdFracSecond0, stdFracSecond9:
832 b = appendNano(b, t.Nanosecond(), std)
833 }
834 }
835 return b
836 }
837
838 var errBad = errors.New("bad value for field")
839
840
841 type ParseError struct {
842 Layout string
843 Value string
844 LayoutElem string
845 ValueElem string
846 Message string
847 }
848
849
850
851 func newParseError(layout, value, layoutElem, valueElem, message string) *ParseError {
852 valueCopy := stringslite.Clone(value)
853 valueElemCopy := stringslite.Clone(valueElem)
854 return &ParseError{layout, valueCopy, layoutElem, valueElemCopy, message}
855 }
856
857
858
859 const (
860 lowerhex = "0123456789abcdef"
861 runeSelf = 0x80
862 runeError = '\uFFFD'
863 )
864
865 func quote(s string) string {
866 buf := make([]byte, 1, len(s)+2)
867 buf[0] = '"'
868 for i, c := range s {
869 if c >= runeSelf || c < ' ' {
870
871
872
873
874
875
876 var width int
877 if c == runeError {
878 width = 1
879 if i+2 < len(s) && s[i:i+3] == string(runeError) {
880 width = 3
881 }
882 } else {
883 width = len(string(c))
884 }
885 for j := 0; j < width; j++ {
886 buf = append(buf, `\x`...)
887 buf = append(buf, lowerhex[s[i+j]>>4])
888 buf = append(buf, lowerhex[s[i+j]&0xF])
889 }
890 } else {
891 if c == '"' || c == '\\' {
892 buf = append(buf, '\\')
893 }
894 buf = append(buf, string(c)...)
895 }
896 }
897 buf = append(buf, '"')
898 return string(buf)
899 }
900
901
902 func (e *ParseError) Error() string {
903 if e.Message == "" {
904 return "parsing time " +
905 quote(e.Value) + " as " +
906 quote(e.Layout) + ": cannot parse " +
907 quote(e.ValueElem) + " as " +
908 quote(e.LayoutElem)
909 }
910 return "parsing time " +
911 quote(e.Value) + e.Message
912 }
913
914
915 func isDigit[bytes []byte | string](s bytes, i int) bool {
916 if len(s) <= i {
917 return false
918 }
919 c := s[i]
920 return '0' <= c && c <= '9'
921 }
922
923
924
925
926 func getnum(s string, fixed bool) (int, string, error) {
927 if !isDigit(s, 0) {
928 return 0, s, errBad
929 }
930 if !isDigit(s, 1) {
931 if fixed {
932 return 0, s, errBad
933 }
934 return int(s[0] - '0'), s[1:], nil
935 }
936 return int(s[0]-'0')*10 + int(s[1]-'0'), s[2:], nil
937 }
938
939
940
941
942 func getnum3(s string, fixed bool) (int, string, error) {
943 var n, i int
944 for i = 0; i < 3 && isDigit(s, i); i++ {
945 n = n*10 + int(s[i]-'0')
946 }
947 if i == 0 || fixed && i != 3 {
948 return 0, s, errBad
949 }
950 return n, s[i:], nil
951 }
952
953 func cutspace(s string) string {
954 for len(s) > 0 && s[0] == ' ' {
955 s = s[1:]
956 }
957 return s
958 }
959
960
961
962 func skip(value, prefix string) (string, error) {
963 for len(prefix) > 0 {
964 if prefix[0] == ' ' {
965 if len(value) > 0 && value[0] != ' ' {
966 return value, errBad
967 }
968 prefix = cutspace(prefix)
969 value = cutspace(value)
970 continue
971 }
972 if len(value) == 0 || value[0] != prefix[0] {
973 return value, errBad
974 }
975 prefix = prefix[1:]
976 value = value[1:]
977 }
978 return value, nil
979 }
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023 func Parse(layout, value string) (Time, error) {
1024
1025 if layout == RFC3339 || layout == RFC3339Nano {
1026 if t, ok := parseRFC3339(value, Local); ok {
1027 return t, nil
1028 }
1029 }
1030 return parse(layout, value, UTC, Local)
1031 }
1032
1033
1034
1035
1036
1037
1038 func ParseInLocation(layout, value string, loc *Location) (Time, error) {
1039
1040 if layout == RFC3339 || layout == RFC3339Nano {
1041 if t, ok := parseRFC3339(value, loc); ok {
1042 return t, nil
1043 }
1044 }
1045 return parse(layout, value, loc, loc)
1046 }
1047
1048 func parse(layout, value string, defaultLocation, local *Location) (Time, error) {
1049 alayout, avalue := layout, value
1050 rangeErrString := ""
1051 amSet := false
1052 pmSet := false
1053
1054
1055 var (
1056 year int
1057 month int = -1
1058 day int = -1
1059 yday int = -1
1060 hour int
1061 min int
1062 sec int
1063 nsec int
1064 z *Location
1065 zoneOffset int = -1
1066 zoneName string
1067 )
1068
1069
1070 for {
1071 var err error
1072 prefix, std, suffix := nextStdChunk(layout)
1073 stdstr := layout[len(prefix) : len(layout)-len(suffix)]
1074 value, err = skip(value, prefix)
1075 if err != nil {
1076 return Time{}, newParseError(alayout, avalue, prefix, value, "")
1077 }
1078 if std == 0 {
1079 if len(value) != 0 {
1080 return Time{}, newParseError(alayout, avalue, "", value, ": extra text: "+quote(value))
1081 }
1082 break
1083 }
1084 layout = suffix
1085 var p string
1086 hold := value
1087 switch std & stdMask {
1088 case stdYear:
1089 if len(value) < 2 {
1090 err = errBad
1091 break
1092 }
1093 p, value = value[0:2], value[2:]
1094 year, err = atoi(p)
1095 if err != nil {
1096 break
1097 }
1098 if year >= 69 {
1099 year += 1900
1100 } else {
1101 year += 2000
1102 }
1103 case stdLongYear:
1104 if len(value) < 4 || !isDigit(value, 0) {
1105 err = errBad
1106 break
1107 }
1108 p, value = value[0:4], value[4:]
1109 year, err = atoi(p)
1110 case stdMonth:
1111 month, value, err = lookup(shortMonthNames, value)
1112 month++
1113 case stdLongMonth:
1114 month, value, err = lookup(longMonthNames, value)
1115 month++
1116 case stdNumMonth, stdZeroMonth:
1117 month, value, err = getnum(value, std == stdZeroMonth)
1118 if err == nil && (month <= 0 || 12 < month) {
1119 rangeErrString = "month"
1120 }
1121 case stdWeekDay:
1122
1123 _, value, err = lookup(shortDayNames, value)
1124 case stdLongWeekDay:
1125 _, value, err = lookup(longDayNames, value)
1126 case stdDay, stdUnderDay, stdZeroDay:
1127 if std == stdUnderDay && len(value) > 0 && value[0] == ' ' {
1128 value = value[1:]
1129 }
1130 day, value, err = getnum(value, std == stdZeroDay)
1131
1132
1133 case stdUnderYearDay, stdZeroYearDay:
1134 for i := 0; i < 2; i++ {
1135 if std == stdUnderYearDay && len(value) > 0 && value[0] == ' ' {
1136 value = value[1:]
1137 }
1138 }
1139 yday, value, err = getnum3(value, std == stdZeroYearDay)
1140
1141
1142 case stdHour:
1143 hour, value, err = getnum(value, false)
1144 if hour < 0 || 24 <= hour {
1145 rangeErrString = "hour"
1146 }
1147 case stdHour12, stdZeroHour12:
1148 hour, value, err = getnum(value, std == stdZeroHour12)
1149 if hour < 0 || 12 < hour {
1150 rangeErrString = "hour"
1151 }
1152 case stdMinute, stdZeroMinute:
1153 min, value, err = getnum(value, std == stdZeroMinute)
1154 if min < 0 || 60 <= min {
1155 rangeErrString = "minute"
1156 }
1157 case stdSecond, stdZeroSecond:
1158 sec, value, err = getnum(value, std == stdZeroSecond)
1159 if err != nil {
1160 break
1161 }
1162 if sec < 0 || 60 <= sec {
1163 rangeErrString = "second"
1164 break
1165 }
1166
1167
1168 if len(value) >= 2 && commaOrPeriod(value[0]) && isDigit(value, 1) {
1169 _, std, _ = nextStdChunk(layout)
1170 std &= stdMask
1171 if std == stdFracSecond0 || std == stdFracSecond9 {
1172
1173 break
1174 }
1175
1176 n := 2
1177 for ; n < len(value) && isDigit(value, n); n++ {
1178 }
1179 nsec, rangeErrString, err = parseNanoseconds(value, n)
1180 value = value[n:]
1181 }
1182 case stdPM:
1183 if len(value) < 2 {
1184 err = errBad
1185 break
1186 }
1187 p, value = value[0:2], value[2:]
1188 switch p {
1189 case "PM":
1190 pmSet = true
1191 case "AM":
1192 amSet = true
1193 default:
1194 err = errBad
1195 }
1196 case stdpm:
1197 if len(value) < 2 {
1198 err = errBad
1199 break
1200 }
1201 p, value = value[0:2], value[2:]
1202 switch p {
1203 case "pm":
1204 pmSet = true
1205 case "am":
1206 amSet = true
1207 default:
1208 err = errBad
1209 }
1210 case stdISO8601TZ, stdISO8601ShortTZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ColonSecondsTZ:
1211 if len(value) >= 1 && value[0] == 'Z' {
1212 value = value[1:]
1213 z = UTC
1214 break
1215 }
1216 fallthrough
1217 case stdNumTZ, stdNumShortTZ, stdNumColonTZ, stdNumSecondsTz, stdNumColonSecondsTZ:
1218 var sign, hour, min, seconds string
1219 if std == stdISO8601ColonTZ || std == stdNumColonTZ {
1220 if len(value) < 6 {
1221 err = errBad
1222 break
1223 }
1224 if value[3] != ':' {
1225 err = errBad
1226 break
1227 }
1228 sign, hour, min, seconds, value = value[0:1], value[1:3], value[4:6], "00", value[6:]
1229 } else if std == stdNumShortTZ || std == stdISO8601ShortTZ {
1230 if len(value) < 3 {
1231 err = errBad
1232 break
1233 }
1234 sign, hour, min, seconds, value = value[0:1], value[1:3], "00", "00", value[3:]
1235 } else if std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ {
1236 if len(value) < 9 {
1237 err = errBad
1238 break
1239 }
1240 if value[3] != ':' || value[6] != ':' {
1241 err = errBad
1242 break
1243 }
1244 sign, hour, min, seconds, value = value[0:1], value[1:3], value[4:6], value[7:9], value[9:]
1245 } else if std == stdISO8601SecondsTZ || std == stdNumSecondsTz {
1246 if len(value) < 7 {
1247 err = errBad
1248 break
1249 }
1250 sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], value[5:7], value[7:]
1251 } else {
1252 if len(value) < 5 {
1253 err = errBad
1254 break
1255 }
1256 sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], "00", value[5:]
1257 }
1258 var hr, mm, ss int
1259 hr, _, err = getnum(hour, true)
1260 if err == nil {
1261 mm, _, err = getnum(min, true)
1262 if err == nil {
1263 ss, _, err = getnum(seconds, true)
1264 }
1265 }
1266
1267
1268
1269
1270 if hr > 24 {
1271 rangeErrString = "time zone offset hour"
1272 }
1273 if mm > 60 {
1274 rangeErrString = "time zone offset minute"
1275 }
1276 if ss > 60 {
1277 rangeErrString = "time zone offset second"
1278 }
1279
1280 zoneOffset = (hr*60+mm)*60 + ss
1281 switch sign[0] {
1282 case '+':
1283 case '-':
1284 zoneOffset = -zoneOffset
1285 default:
1286 err = errBad
1287 }
1288 case stdTZ:
1289
1290 if len(value) >= 3 && value[0:3] == "UTC" {
1291 z = UTC
1292 value = value[3:]
1293 break
1294 }
1295 n, ok := parseTimeZone(value)
1296 if !ok {
1297 err = errBad
1298 break
1299 }
1300 zoneName, value = value[:n], value[n:]
1301
1302 case stdFracSecond0:
1303
1304
1305 ndigit := 1 + digitsLen(std)
1306 if len(value) < ndigit {
1307 err = errBad
1308 break
1309 }
1310 nsec, rangeErrString, err = parseNanoseconds(value, ndigit)
1311 value = value[ndigit:]
1312
1313 case stdFracSecond9:
1314 if len(value) < 2 || !commaOrPeriod(value[0]) || value[1] < '0' || '9' < value[1] {
1315
1316 break
1317 }
1318
1319
1320 i := 0
1321 for i+1 < len(value) && '0' <= value[i+1] && value[i+1] <= '9' {
1322 i++
1323 }
1324 nsec, rangeErrString, err = parseNanoseconds(value, 1+i)
1325 value = value[1+i:]
1326 }
1327 if rangeErrString != "" {
1328 return Time{}, newParseError(alayout, avalue, stdstr, value, ": "+rangeErrString+" out of range")
1329 }
1330 if err != nil {
1331 return Time{}, newParseError(alayout, avalue, stdstr, hold, "")
1332 }
1333 }
1334 if pmSet && hour < 12 {
1335 hour += 12
1336 } else if amSet && hour == 12 {
1337 hour = 0
1338 }
1339
1340
1341 if yday >= 0 {
1342 var d int
1343 var m int
1344 if isLeap(year) {
1345 if yday == 31+29 {
1346 m = int(February)
1347 d = 29
1348 } else if yday > 31+29 {
1349 yday--
1350 }
1351 }
1352 if yday < 1 || yday > 365 {
1353 return Time{}, newParseError(alayout, avalue, "", value, ": day-of-year out of range")
1354 }
1355 if m == 0 {
1356 m = (yday-1)/31 + 1
1357 if daysBefore(Month(m+1)) < yday {
1358 m++
1359 }
1360 d = yday - daysBefore(Month(m))
1361 }
1362
1363
1364 if month >= 0 && month != m {
1365 return Time{}, newParseError(alayout, avalue, "", value, ": day-of-year does not match month")
1366 }
1367 month = m
1368 if day >= 0 && day != d {
1369 return Time{}, newParseError(alayout, avalue, "", value, ": day-of-year does not match day")
1370 }
1371 day = d
1372 } else {
1373 if month < 0 {
1374 month = int(January)
1375 }
1376 if day < 0 {
1377 day = 1
1378 }
1379 }
1380
1381
1382 if day < 1 || day > daysIn(Month(month), year) {
1383 return Time{}, newParseError(alayout, avalue, "", value, ": day out of range")
1384 }
1385
1386 if z != nil {
1387 return Date(year, Month(month), day, hour, min, sec, nsec, z), nil
1388 }
1389
1390 if zoneOffset != -1 {
1391 t := Date(year, Month(month), day, hour, min, sec, nsec, UTC)
1392 t.addSec(-int64(zoneOffset))
1393
1394
1395
1396 name, offset, _, _, _ := local.lookup(t.unixSec())
1397 if offset == zoneOffset && (zoneName == "" || name == zoneName) {
1398 t.setLoc(local)
1399 return t, nil
1400 }
1401
1402
1403 zoneNameCopy := stringslite.Clone(zoneName)
1404 t.setLoc(FixedZone(zoneNameCopy, zoneOffset))
1405 return t, nil
1406 }
1407
1408 if zoneName != "" {
1409 t := Date(year, Month(month), day, hour, min, sec, nsec, UTC)
1410
1411
1412 offset, ok := local.lookupName(zoneName, t.unixSec())
1413 if ok {
1414 t.addSec(-int64(offset))
1415 t.setLoc(local)
1416 return t, nil
1417 }
1418
1419
1420 if len(zoneName) > 3 && zoneName[:3] == "GMT" {
1421 offset, _ = atoi(zoneName[3:])
1422 offset *= 3600
1423 }
1424 zoneNameCopy := stringslite.Clone(zoneName)
1425 t.setLoc(FixedZone(zoneNameCopy, offset))
1426 return t, nil
1427 }
1428
1429
1430 return Date(year, Month(month), day, hour, min, sec, nsec, defaultLocation), nil
1431 }
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443 func parseTimeZone(value string) (length int, ok bool) {
1444 if len(value) < 3 {
1445 return 0, false
1446 }
1447
1448 if len(value) >= 4 && (value[:4] == "ChST" || value[:4] == "MeST") {
1449 return 4, true
1450 }
1451
1452 if value[:3] == "GMT" {
1453 length = parseGMT(value)
1454 return length, true
1455 }
1456
1457 if value[0] == '+' || value[0] == '-' {
1458 length = parseSignedOffset(value)
1459 ok := length > 0
1460 return length, ok
1461 }
1462
1463 var nUpper int
1464 for nUpper = 0; nUpper < 6; nUpper++ {
1465 if nUpper >= len(value) {
1466 break
1467 }
1468 if c := value[nUpper]; c < 'A' || 'Z' < c {
1469 break
1470 }
1471 }
1472 switch nUpper {
1473 case 0, 1, 2, 6:
1474 return 0, false
1475 case 5:
1476 if value[4] == 'T' {
1477 return 5, true
1478 }
1479 case 4:
1480
1481 if value[3] == 'T' || value[:4] == "WITA" {
1482 return 4, true
1483 }
1484 case 3:
1485 return 3, true
1486 }
1487 return 0, false
1488 }
1489
1490
1491
1492
1493 func parseGMT(value string) int {
1494 value = value[3:]
1495 if len(value) == 0 {
1496 return 3
1497 }
1498
1499 return 3 + parseSignedOffset(value)
1500 }
1501
1502
1503
1504
1505 func parseSignedOffset(value string) int {
1506 sign := value[0]
1507 if sign != '-' && sign != '+' {
1508 return 0
1509 }
1510 x, rem, err := leadingInt(value[1:])
1511
1512
1513 if err != nil || value[1:] == rem {
1514 return 0
1515 }
1516 if x > 23 {
1517 return 0
1518 }
1519 return len(value) - len(rem)
1520 }
1521
1522 func commaOrPeriod(b byte) bool {
1523 return b == '.' || b == ','
1524 }
1525
1526 func parseNanoseconds[bytes []byte | string](value bytes, nbytes int) (ns int, rangeErrString string, err error) {
1527 if !commaOrPeriod(value[0]) {
1528 err = errBad
1529 return
1530 }
1531 if nbytes > 10 {
1532 value = value[:10]
1533 nbytes = 10
1534 }
1535 if ns, err = atoi(value[1:nbytes]); err != nil {
1536 return
1537 }
1538 if ns < 0 {
1539 rangeErrString = "fractional second"
1540 return
1541 }
1542
1543
1544 scaleDigits := 10 - nbytes
1545 for i := 0; i < scaleDigits; i++ {
1546 ns *= 10
1547 }
1548 return
1549 }
1550
1551 var errLeadingInt = errors.New("time: bad [0-9]*")
1552
1553
1554 func leadingInt[bytes []byte | string](s bytes) (x uint64, rem bytes, err error) {
1555 i := 0
1556 for ; i < len(s); i++ {
1557 c := s[i]
1558 if c < '0' || c > '9' {
1559 break
1560 }
1561 if x > 1<<63/10 {
1562
1563 return 0, rem, errLeadingInt
1564 }
1565 x = x*10 + uint64(c) - '0'
1566 if x > 1<<63 {
1567
1568 return 0, rem, errLeadingInt
1569 }
1570 }
1571 return x, s[i:], nil
1572 }
1573
1574
1575
1576
1577 func leadingFraction(s string) (x uint64, scale float64, rem string) {
1578 i := 0
1579 scale = 1
1580 overflow := false
1581 for ; i < len(s); i++ {
1582 c := s[i]
1583 if c < '0' || c > '9' {
1584 break
1585 }
1586 if overflow {
1587 continue
1588 }
1589 if x > (1<<63-1)/10 {
1590
1591 overflow = true
1592 continue
1593 }
1594 y := x*10 + uint64(c) - '0'
1595 if y > 1<<63 {
1596 overflow = true
1597 continue
1598 }
1599 x = y
1600 scale *= 10
1601 }
1602 return x, scale, s[i:]
1603 }
1604
1605 var unitMap = map[string]uint64{
1606 "ns": uint64(Nanosecond),
1607 "us": uint64(Microsecond),
1608 "µs": uint64(Microsecond),
1609 "μs": uint64(Microsecond),
1610 "ms": uint64(Millisecond),
1611 "s": uint64(Second),
1612 "m": uint64(Minute),
1613 "h": uint64(Hour),
1614 }
1615
1616
1617
1618
1619
1620
1621 func ParseDuration(s string) (Duration, error) {
1622
1623 orig := s
1624 var d uint64
1625 neg := false
1626
1627
1628 if s != "" {
1629 c := s[0]
1630 if c == '-' || c == '+' {
1631 neg = c == '-'
1632 s = s[1:]
1633 }
1634 }
1635
1636 if s == "0" {
1637 return 0, nil
1638 }
1639 if s == "" {
1640 return 0, errors.New("time: invalid duration " + quote(orig))
1641 }
1642 for s != "" {
1643 var (
1644 v, f uint64
1645 scale float64 = 1
1646 )
1647
1648 var err error
1649
1650
1651 if !(s[0] == '.' || '0' <= s[0] && s[0] <= '9') {
1652 return 0, errors.New("time: invalid duration " + quote(orig))
1653 }
1654
1655 pl := len(s)
1656 v, s, err = leadingInt(s)
1657 if err != nil {
1658 return 0, errors.New("time: invalid duration " + quote(orig))
1659 }
1660 pre := pl != len(s)
1661
1662
1663 post := false
1664 if s != "" && s[0] == '.' {
1665 s = s[1:]
1666 pl := len(s)
1667 f, scale, s = leadingFraction(s)
1668 post = pl != len(s)
1669 }
1670 if !pre && !post {
1671
1672 return 0, errors.New("time: invalid duration " + quote(orig))
1673 }
1674
1675
1676 i := 0
1677 for ; i < len(s); i++ {
1678 c := s[i]
1679 if c == '.' || '0' <= c && c <= '9' {
1680 break
1681 }
1682 }
1683 if i == 0 {
1684 return 0, errors.New("time: missing unit in duration " + quote(orig))
1685 }
1686 u := s[:i]
1687 s = s[i:]
1688 unit, ok := unitMap[u]
1689 if !ok {
1690 return 0, errors.New("time: unknown unit " + quote(u) + " in duration " + quote(orig))
1691 }
1692 if v > 1<<63/unit {
1693
1694 return 0, errors.New("time: invalid duration " + quote(orig))
1695 }
1696 v *= unit
1697 if f > 0 {
1698
1699
1700 v += uint64(float64(f) * (float64(unit) / scale))
1701 if v > 1<<63 {
1702
1703 return 0, errors.New("time: invalid duration " + quote(orig))
1704 }
1705 }
1706 d += v
1707 if d > 1<<63 {
1708 return 0, errors.New("time: invalid duration " + quote(orig))
1709 }
1710 }
1711 if neg {
1712 return -Duration(d), nil
1713 }
1714 if d > 1<<63-1 {
1715 return 0, errors.New("time: invalid duration " + quote(orig))
1716 }
1717 return Duration(d), nil
1718 }
1719
View as plain text