@@ -48,6 +48,10 @@ import "errors"
48
48
// The recognized day of week formats are "Mon" and "Monday".
49
49
// The recognized month formats are "Jan" and "January".
50
50
//
51
+ // The formats 2, _2, and 02 are unpadded, space-padded, and zero-padded
52
+ // day of month. The formats __2 and 002 are space-padded and zero-padded
53
+ // three-character day of year; there is no unpadded day of year format.
54
+ //
51
55
// Text in the format string that is not recognized as part of the reference
52
56
// time is echoed verbatim during Format and expected to appear verbatim
53
57
// in the input to Parse.
@@ -96,6 +100,8 @@ const (
96
100
stdDay // "2"
97
101
stdUnderDay // "_2"
98
102
stdZeroDay // "02"
103
+ stdUnderYearDay // "__2"
104
+ stdZeroYearDay // "002"
99
105
stdHour = iota + stdNeedClock // "15"
100
106
stdHour12 // "3"
101
107
stdZeroHour12 // "03"
@@ -170,10 +176,13 @@ func nextStdChunk(layout string) (prefix string, std int, suffix string) {
170
176
}
171
177
}
172
178
173
- case '0' : // 01, 02, 03, 04, 05, 06
179
+ case '0' : // 01, 02, 03, 04, 05, 06, 002
174
180
if len (layout ) >= i + 2 && '1' <= layout [i + 1 ] && layout [i + 1 ] <= '6' {
175
181
return layout [0 :i ], std0x [layout [i + 1 ]- '1' ], layout [i + 2 :]
176
182
}
183
+ if len (layout ) >= i + 3 && layout [i + 1 ] == '0' && layout [i + 2 ] == '2' {
184
+ return layout [0 :i ], stdZeroYearDay , layout [i + 3 :]
185
+ }
177
186
178
187
case '1' : // 15, 1
179
188
if len (layout ) >= i + 2 && layout [i + 1 ] == '5' {
@@ -187,14 +196,17 @@ func nextStdChunk(layout string) (prefix string, std int, suffix string) {
187
196
}
188
197
return layout [0 :i ], stdDay , layout [i + 1 :]
189
198
190
- case '_' : // _2, _2006
199
+ case '_' : // _2, _2006, __2
191
200
if len (layout ) >= i + 2 && layout [i + 1 ] == '2' {
192
201
//_2006 is really a literal _, followed by stdLongYear
193
202
if len (layout ) >= i + 5 && layout [i + 1 :i + 5 ] == "2006" {
194
203
return layout [0 : i + 1 ], stdLongYear , layout [i + 5 :]
195
204
}
196
205
return layout [0 :i ], stdUnderDay , layout [i + 2 :]
197
206
}
207
+ if len (layout ) >= i + 3 && layout [i + 1 ] == '_' && layout [i + 2 ] == '2' {
208
+ return layout [0 :i ], stdUnderYearDay , layout [i + 3 :]
209
+ }
198
210
199
211
case '3' :
200
212
return layout [0 :i ], stdHour12 , layout [i + 1 :]
@@ -503,6 +515,7 @@ func (t Time) AppendFormat(b []byte, layout string) []byte {
503
515
year int = - 1
504
516
month Month
505
517
day int
518
+ yday int
506
519
hour int = - 1
507
520
min int
508
521
sec int
@@ -520,7 +533,8 @@ func (t Time) AppendFormat(b []byte, layout string) []byte {
520
533
521
534
// Compute year, month, day if needed.
522
535
if year < 0 && std & stdNeedDate != 0 {
523
- year , month , day , _ = absDate (abs , true )
536
+ year , month , day , yday = absDate (abs , true )
537
+ yday ++
524
538
}
525
539
526
540
// Compute hour, minute, second if needed.
@@ -560,6 +574,16 @@ func (t Time) AppendFormat(b []byte, layout string) []byte {
560
574
b = appendInt (b , day , 0 )
561
575
case stdZeroDay :
562
576
b = appendInt (b , day , 2 )
577
+ case stdUnderYearDay :
578
+ if yday < 100 {
579
+ b = append (b , ' ' )
580
+ if yday < 10 {
581
+ b = append (b , ' ' )
582
+ }
583
+ }
584
+ b = appendInt (b , yday , 0 )
585
+ case stdZeroYearDay :
586
+ b = appendInt (b , yday , 3 )
563
587
case stdHour :
564
588
b = appendInt (b , hour , 2 )
565
589
case stdHour12 :
@@ -688,7 +712,7 @@ func isDigit(s string, i int) bool {
688
712
return '0' <= c && c <= '9'
689
713
}
690
714
691
- // getnum parses s[0:1] or s[0:2] (fixed forces the latter )
715
+ // getnum parses s[0:1] or s[0:2] (fixed forces s[0:2] )
692
716
// as a decimal integer and returns the integer and the
693
717
// remainder of the string.
694
718
func getnum (s string , fixed bool ) (int , string , error ) {
@@ -704,6 +728,20 @@ func getnum(s string, fixed bool) (int, string, error) {
704
728
return int (s [0 ]- '0' )* 10 + int (s [1 ]- '0' ), s [2 :], nil
705
729
}
706
730
731
+ // getnum3 parses s[0:1], s[0:2], or s[0:3] (fixed forces s[0:3])
732
+ // as a decimal integer and returns the integer and the remainder
733
+ // of the string.
734
+ func getnum3 (s string , fixed bool ) (int , string , error ) {
735
+ var n , i int
736
+ for i = 0 ; i < 3 && isDigit (s , i ); i ++ {
737
+ n = n * 10 + int (s [i ]- '0' )
738
+ }
739
+ if i == 0 || fixed && i != 3 {
740
+ return 0 , s , errBad
741
+ }
742
+ return n , s [i :], nil
743
+ }
744
+
707
745
func cutspace (s string ) string {
708
746
for len (s ) > 0 && s [0 ] == ' ' {
709
747
s = s [1 :]
@@ -792,8 +830,9 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
792
830
// Time being constructed.
793
831
var (
794
832
year int
795
- month int = 1 // January
796
- day int = 1
833
+ month int = - 1
834
+ day int = - 1
835
+ yday int = - 1
797
836
hour int
798
837
min int
799
838
sec int
@@ -861,10 +900,17 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
861
900
value = value [1 :]
862
901
}
863
902
day , value , err = getnum (value , std == stdZeroDay )
864
- if day < 0 {
865
- // Note that we allow any one- or two-digit day here.
866
- rangeErrString = "day"
903
+ // Note that we allow any one- or two-digit day here.
904
+ // The month, day, year combination is validated after we've completed parsing.
905
+ case stdUnderYearDay , stdZeroYearDay :
906
+ for i := 0 ; i < 2 ; i ++ {
907
+ if std == stdUnderYearDay && len (value ) > 0 && value [0 ] == ' ' {
908
+ value = value [1 :]
909
+ }
867
910
}
911
+ yday , value , err = getnum3 (value , std == stdZeroYearDay )
912
+ // Note that we allow any one-, two-, or three-digit year-day here.
913
+ // The year-day, year combination is validated after we've completed parsing.
868
914
case stdHour :
869
915
hour , value , err = getnum (value , false )
870
916
if hour < 0 || 24 <= hour {
@@ -1044,6 +1090,47 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
1044
1090
hour = 0
1045
1091
}
1046
1092
1093
+ // Convert yday to day, month.
1094
+ if yday >= 0 {
1095
+ var d int
1096
+ var m int
1097
+ if isLeap (year ) {
1098
+ if yday == 31 + 29 {
1099
+ m = int (February )
1100
+ d = 29
1101
+ } else if yday > 31 + 29 {
1102
+ yday --
1103
+ }
1104
+ }
1105
+ if yday < 1 || yday > 365 {
1106
+ return Time {}, & ParseError {alayout , avalue , "" , value , ": day-of-year out of range" }
1107
+ }
1108
+ if m == 0 {
1109
+ m = yday / 31 + 1
1110
+ if int (daysBefore [m ]) < yday {
1111
+ m ++
1112
+ }
1113
+ d = yday - int (daysBefore [m - 1 ])
1114
+ }
1115
+ // If month, day already seen, yday's m, d must match.
1116
+ // Otherwise, set them from m, d.
1117
+ if month >= 0 && month != m {
1118
+ return Time {}, & ParseError {alayout , avalue , "" , value , ": day-of-year does not match month" }
1119
+ }
1120
+ month = m
1121
+ if day >= 0 && day != d {
1122
+ return Time {}, & ParseError {alayout , avalue , "" , value , ": day-of-year does not match day" }
1123
+ }
1124
+ day = d
1125
+ } else {
1126
+ if month < 0 {
1127
+ month = int (January )
1128
+ }
1129
+ if day < 0 {
1130
+ day = 1
1131
+ }
1132
+ }
1133
+
1047
1134
// Validate the day of the month.
1048
1135
if day < 1 || day > daysIn (Month (month ), year ) {
1049
1136
return Time {}, & ParseError {alayout , avalue , "" , value , ": day out of range" }
0 commit comments