30 // date-month = 2DIGIT ; 01-12 |
32 // date-month = 2DIGIT ; 01-12 |
31 // date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year |
33 // date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year |
32 var date LocalDate |
34 var date LocalDate |
33 |
35 |
34 if len(b) != 10 || b[4] != '-' || b[7] != '-' { |
36 if len(b) != 10 || b[4] != '-' || b[7] != '-' { |
35 return date, newDecodeError(b, "dates are expected to have the format YYYY-MM-DD") |
37 return date, unstable.NewParserError(b, "dates are expected to have the format YYYY-MM-DD") |
36 } |
38 } |
37 |
39 |
38 var err error |
40 var err error |
39 |
41 |
40 date.Year, err = parseDecimalDigits(b[0:4]) |
42 date.Year, err = parseDecimalDigits(b[0:4]) |
51 if err != nil { |
53 if err != nil { |
52 return LocalDate{}, err |
54 return LocalDate{}, err |
53 } |
55 } |
54 |
56 |
55 if !isValidDate(date.Year, date.Month, date.Day) { |
57 if !isValidDate(date.Year, date.Month, date.Day) { |
56 return LocalDate{}, newDecodeError(b, "impossible date") |
58 return LocalDate{}, unstable.NewParserError(b, "impossible date") |
57 } |
59 } |
58 |
60 |
59 return date, nil |
61 return date, nil |
60 } |
62 } |
61 |
63 |
62 func parseDecimalDigits(b []byte) (int, error) { |
64 func parseDecimalDigits(b []byte) (int, error) { |
63 v := 0 |
65 v := 0 |
64 |
66 |
65 for i, c := range b { |
67 for i, c := range b { |
66 if c < '0' || c > '9' { |
68 if c < '0' || c > '9' { |
67 return 0, newDecodeError(b[i:i+1], "expected digit (0-9)") |
69 return 0, unstable.NewParserError(b[i:i+1], "expected digit (0-9)") |
68 } |
70 } |
69 v *= 10 |
71 v *= 10 |
70 v += int(c - '0') |
72 v += int(c - '0') |
71 } |
73 } |
72 |
74 |
95 b = b[1:] |
97 b = b[1:] |
96 zone = time.UTC |
98 zone = time.UTC |
97 } else { |
99 } else { |
98 const dateTimeByteLen = 6 |
100 const dateTimeByteLen = 6 |
99 if len(b) != dateTimeByteLen { |
101 if len(b) != dateTimeByteLen { |
100 return time.Time{}, newDecodeError(b, "invalid date-time timezone") |
102 return time.Time{}, unstable.NewParserError(b, "invalid date-time timezone") |
101 } |
103 } |
102 var direction int |
104 var direction int |
103 switch b[0] { |
105 switch b[0] { |
104 case '-': |
106 case '-': |
105 direction = -1 |
107 direction = -1 |
106 case '+': |
108 case '+': |
107 direction = +1 |
109 direction = +1 |
108 default: |
110 default: |
109 return time.Time{}, newDecodeError(b[:1], "invalid timezone offset character") |
111 return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset character") |
110 } |
112 } |
111 |
113 |
112 if b[3] != ':' { |
114 if b[3] != ':' { |
113 return time.Time{}, newDecodeError(b[3:4], "expected a : separator") |
115 return time.Time{}, unstable.NewParserError(b[3:4], "expected a : separator") |
114 } |
116 } |
115 |
117 |
116 hours, err := parseDecimalDigits(b[1:3]) |
118 hours, err := parseDecimalDigits(b[1:3]) |
117 if err != nil { |
119 if err != nil { |
118 return time.Time{}, err |
120 return time.Time{}, err |
119 } |
121 } |
120 if hours > 23 { |
122 if hours > 23 { |
121 return time.Time{}, newDecodeError(b[:1], "invalid timezone offset hours") |
123 return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset hours") |
122 } |
124 } |
123 |
125 |
124 minutes, err := parseDecimalDigits(b[4:6]) |
126 minutes, err := parseDecimalDigits(b[4:6]) |
125 if err != nil { |
127 if err != nil { |
126 return time.Time{}, err |
128 return time.Time{}, err |
127 } |
129 } |
128 if minutes > 59 { |
130 if minutes > 59 { |
129 return time.Time{}, newDecodeError(b[:1], "invalid timezone offset minutes") |
131 return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset minutes") |
130 } |
132 } |
131 |
133 |
132 seconds := direction * (hours*3600 + minutes*60) |
134 seconds := direction * (hours*3600 + minutes*60) |
133 if seconds == 0 { |
135 if seconds == 0 { |
134 zone = time.UTC |
136 zone = time.UTC |
158 func parseLocalDateTime(b []byte) (LocalDateTime, []byte, error) { |
160 func parseLocalDateTime(b []byte) (LocalDateTime, []byte, error) { |
159 var dt LocalDateTime |
161 var dt LocalDateTime |
160 |
162 |
161 const localDateTimeByteMinLen = 11 |
163 const localDateTimeByteMinLen = 11 |
162 if len(b) < localDateTimeByteMinLen { |
164 if len(b) < localDateTimeByteMinLen { |
163 return dt, nil, newDecodeError(b, "local datetimes are expected to have the format YYYY-MM-DDTHH:MM:SS[.NNNNNNNNN]") |
165 return dt, nil, unstable.NewParserError(b, "local datetimes are expected to have the format YYYY-MM-DDTHH:MM:SS[.NNNNNNNNN]") |
164 } |
166 } |
165 |
167 |
166 date, err := parseLocalDate(b[:10]) |
168 date, err := parseLocalDate(b[:10]) |
167 if err != nil { |
169 if err != nil { |
168 return dt, nil, err |
170 return dt, nil, err |
169 } |
171 } |
170 dt.LocalDate = date |
172 dt.LocalDate = date |
171 |
173 |
172 sep := b[10] |
174 sep := b[10] |
173 if sep != 'T' && sep != ' ' && sep != 't' { |
175 if sep != 'T' && sep != ' ' && sep != 't' { |
174 return dt, nil, newDecodeError(b[10:11], "datetime separator is expected to be T or a space") |
176 return dt, nil, unstable.NewParserError(b[10:11], "datetime separator is expected to be T or a space") |
175 } |
177 } |
176 |
178 |
177 t, rest, err := parseLocalTime(b[11:]) |
179 t, rest, err := parseLocalTime(b[11:]) |
178 if err != nil { |
180 if err != nil { |
179 return dt, nil, err |
181 return dt, nil, err |
193 ) |
195 ) |
194 |
196 |
195 // check if b matches to have expected format HH:MM:SS[.NNNNNN] |
197 // check if b matches to have expected format HH:MM:SS[.NNNNNN] |
196 const localTimeByteLen = 8 |
198 const localTimeByteLen = 8 |
197 if len(b) < localTimeByteLen { |
199 if len(b) < localTimeByteLen { |
198 return t, nil, newDecodeError(b, "times are expected to have the format HH:MM:SS[.NNNNNN]") |
200 return t, nil, unstable.NewParserError(b, "times are expected to have the format HH:MM:SS[.NNNNNN]") |
199 } |
201 } |
200 |
202 |
201 var err error |
203 var err error |
202 |
204 |
203 t.Hour, err = parseDecimalDigits(b[0:2]) |
205 t.Hour, err = parseDecimalDigits(b[0:2]) |
204 if err != nil { |
206 if err != nil { |
205 return t, nil, err |
207 return t, nil, err |
206 } |
208 } |
207 |
209 |
208 if t.Hour > 23 { |
210 if t.Hour > 23 { |
209 return t, nil, newDecodeError(b[0:2], "hour cannot be greater 23") |
211 return t, nil, unstable.NewParserError(b[0:2], "hour cannot be greater 23") |
210 } |
212 } |
211 if b[2] != ':' { |
213 if b[2] != ':' { |
212 return t, nil, newDecodeError(b[2:3], "expecting colon between hours and minutes") |
214 return t, nil, unstable.NewParserError(b[2:3], "expecting colon between hours and minutes") |
213 } |
215 } |
214 |
216 |
215 t.Minute, err = parseDecimalDigits(b[3:5]) |
217 t.Minute, err = parseDecimalDigits(b[3:5]) |
216 if err != nil { |
218 if err != nil { |
217 return t, nil, err |
219 return t, nil, err |
218 } |
220 } |
219 if t.Minute > 59 { |
221 if t.Minute > 59 { |
220 return t, nil, newDecodeError(b[3:5], "minutes cannot be greater 59") |
222 return t, nil, unstable.NewParserError(b[3:5], "minutes cannot be greater 59") |
221 } |
223 } |
222 if b[5] != ':' { |
224 if b[5] != ':' { |
223 return t, nil, newDecodeError(b[5:6], "expecting colon between minutes and seconds") |
225 return t, nil, unstable.NewParserError(b[5:6], "expecting colon between minutes and seconds") |
224 } |
226 } |
225 |
227 |
226 t.Second, err = parseDecimalDigits(b[6:8]) |
228 t.Second, err = parseDecimalDigits(b[6:8]) |
227 if err != nil { |
229 if err != nil { |
228 return t, nil, err |
230 return t, nil, err |
229 } |
231 } |
230 |
232 |
231 if t.Second > 60 { |
233 if t.Second > 60 { |
232 return t, nil, newDecodeError(b[6:8], "seconds cannot be greater 60") |
234 return t, nil, unstable.NewParserError(b[6:8], "seconds cannot be greater 60") |
233 } |
235 } |
234 |
236 |
235 b = b[8:] |
237 b = b[8:] |
236 |
238 |
237 if len(b) >= 1 && b[0] == '.' { |
239 if len(b) >= 1 && b[0] == '.' { |
287 if err != nil { |
289 if err != nil { |
288 return 0, err |
290 return 0, err |
289 } |
291 } |
290 |
292 |
291 if cleaned[0] == '.' { |
293 if cleaned[0] == '.' { |
292 return 0, newDecodeError(b, "float cannot start with a dot") |
294 return 0, unstable.NewParserError(b, "float cannot start with a dot") |
293 } |
295 } |
294 |
296 |
295 if cleaned[len(cleaned)-1] == '.' { |
297 if cleaned[len(cleaned)-1] == '.' { |
296 return 0, newDecodeError(b, "float cannot end with a dot") |
298 return 0, unstable.NewParserError(b, "float cannot end with a dot") |
297 } |
299 } |
298 |
300 |
299 dotAlreadySeen := false |
301 dotAlreadySeen := false |
300 for i, c := range cleaned { |
302 for i, c := range cleaned { |
301 if c == '.' { |
303 if c == '.' { |
302 if dotAlreadySeen { |
304 if dotAlreadySeen { |
303 return 0, newDecodeError(b[i:i+1], "float can have at most one decimal point") |
305 return 0, unstable.NewParserError(b[i:i+1], "float can have at most one decimal point") |
304 } |
306 } |
305 if !isDigit(cleaned[i-1]) { |
307 if !isDigit(cleaned[i-1]) { |
306 return 0, newDecodeError(b[i-1:i+1], "float decimal point must be preceded by a digit") |
308 return 0, unstable.NewParserError(b[i-1:i+1], "float decimal point must be preceded by a digit") |
307 } |
309 } |
308 if !isDigit(cleaned[i+1]) { |
310 if !isDigit(cleaned[i+1]) { |
309 return 0, newDecodeError(b[i:i+2], "float decimal point must be followed by a digit") |
311 return 0, unstable.NewParserError(b[i:i+2], "float decimal point must be followed by a digit") |
310 } |
312 } |
311 dotAlreadySeen = true |
313 dotAlreadySeen = true |
312 } |
314 } |
313 } |
315 } |
314 |
316 |
315 start := 0 |
317 start := 0 |
316 if cleaned[0] == '+' || cleaned[0] == '-' { |
318 if cleaned[0] == '+' || cleaned[0] == '-' { |
317 start = 1 |
319 start = 1 |
318 } |
320 } |
319 if cleaned[start] == '0' && isDigit(cleaned[start+1]) { |
321 if cleaned[start] == '0' && isDigit(cleaned[start+1]) { |
320 return 0, newDecodeError(b, "float integer part cannot have leading zeroes") |
322 return 0, unstable.NewParserError(b, "float integer part cannot have leading zeroes") |
321 } |
323 } |
322 |
324 |
323 f, err := strconv.ParseFloat(string(cleaned), 64) |
325 f, err := strconv.ParseFloat(string(cleaned), 64) |
324 if err != nil { |
326 if err != nil { |
325 return 0, newDecodeError(b, "unable to parse float: %w", err) |
327 return 0, unstable.NewParserError(b, "unable to parse float: %w", err) |
326 } |
328 } |
327 |
329 |
328 return f, nil |
330 return f, nil |
329 } |
331 } |
330 |
332 |
385 if isSign(cleaned[0]) { |
387 if isSign(cleaned[0]) { |
386 startIdx++ |
388 startIdx++ |
387 } |
389 } |
388 |
390 |
389 if len(cleaned) > startIdx+1 && cleaned[startIdx] == '0' { |
391 if len(cleaned) > startIdx+1 && cleaned[startIdx] == '0' { |
390 return 0, newDecodeError(b, "leading zero not allowed on decimal number") |
392 return 0, unstable.NewParserError(b, "leading zero not allowed on decimal number") |
391 } |
393 } |
392 |
394 |
393 i, err := strconv.ParseInt(string(cleaned), 10, 64) |
395 i, err := strconv.ParseInt(string(cleaned), 10, 64) |
394 if err != nil { |
396 if err != nil { |
395 return 0, newDecodeError(b, "couldn't parse decimal number: %w", err) |
397 return 0, unstable.NewParserError(b, "couldn't parse decimal number: %w", err) |
396 } |
398 } |
397 |
399 |
398 return i, nil |
400 return i, nil |
399 } |
401 } |
400 |
402 |
407 if len(b) == start { |
409 if len(b) == start { |
408 return b, nil |
410 return b, nil |
409 } |
411 } |
410 |
412 |
411 if b[start] == '_' { |
413 if b[start] == '_' { |
412 return nil, newDecodeError(b[start:start+1], "number cannot start with underscore") |
414 return nil, unstable.NewParserError(b[start:start+1], "number cannot start with underscore") |
413 } |
415 } |
414 |
416 |
415 if b[len(b)-1] == '_' { |
417 if b[len(b)-1] == '_' { |
416 return nil, newDecodeError(b[len(b)-1:], "number cannot end with underscore") |
418 return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore") |
417 } |
419 } |
418 |
420 |
419 // fast path |
421 // fast path |
420 i := 0 |
422 i := 0 |
421 for ; i < len(b); i++ { |
423 for ; i < len(b); i++ { |
447 return cleaned, nil |
449 return cleaned, nil |
448 } |
450 } |
449 |
451 |
450 func checkAndRemoveUnderscoresFloats(b []byte) ([]byte, error) { |
452 func checkAndRemoveUnderscoresFloats(b []byte) ([]byte, error) { |
451 if b[0] == '_' { |
453 if b[0] == '_' { |
452 return nil, newDecodeError(b[0:1], "number cannot start with underscore") |
454 return nil, unstable.NewParserError(b[0:1], "number cannot start with underscore") |
453 } |
455 } |
454 |
456 |
455 if b[len(b)-1] == '_' { |
457 if b[len(b)-1] == '_' { |
456 return nil, newDecodeError(b[len(b)-1:], "number cannot end with underscore") |
458 return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore") |
457 } |
459 } |
458 |
460 |
459 // fast path |
461 // fast path |
460 i := 0 |
462 i := 0 |
461 for ; i < len(b); i++ { |
463 for ; i < len(b); i++ { |
474 c := b[i] |
476 c := b[i] |
475 |
477 |
476 switch c { |
478 switch c { |
477 case '_': |
479 case '_': |
478 if !before { |
480 if !before { |
479 return nil, newDecodeError(b[i-1:i+1], "number must have at least one digit between underscores") |
481 return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores") |
480 } |
482 } |
481 if i < len(b)-1 && (b[i+1] == 'e' || b[i+1] == 'E') { |
483 if i < len(b)-1 && (b[i+1] == 'e' || b[i+1] == 'E') { |
482 return nil, newDecodeError(b[i+1:i+2], "cannot have underscore before exponent") |
484 return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore before exponent") |
483 } |
485 } |
484 before = false |
486 before = false |
485 case '+', '-': |
487 case '+', '-': |
486 // signed exponents |
488 // signed exponents |
487 cleaned = append(cleaned, c) |
489 cleaned = append(cleaned, c) |
488 before = false |
490 before = false |
489 case 'e', 'E': |
491 case 'e', 'E': |
490 if i < len(b)-1 && b[i+1] == '_' { |
492 if i < len(b)-1 && b[i+1] == '_' { |
491 return nil, newDecodeError(b[i+1:i+2], "cannot have underscore after exponent") |
493 return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after exponent") |
492 } |
494 } |
493 cleaned = append(cleaned, c) |
495 cleaned = append(cleaned, c) |
494 case '.': |
496 case '.': |
495 if i < len(b)-1 && b[i+1] == '_' { |
497 if i < len(b)-1 && b[i+1] == '_' { |
496 return nil, newDecodeError(b[i+1:i+2], "cannot have underscore after decimal point") |
498 return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after decimal point") |
497 } |
499 } |
498 if i > 0 && b[i-1] == '_' { |
500 if i > 0 && b[i-1] == '_' { |
499 return nil, newDecodeError(b[i-1:i], "cannot have underscore before decimal point") |
501 return nil, unstable.NewParserError(b[i-1:i], "cannot have underscore before decimal point") |
500 } |
502 } |
501 cleaned = append(cleaned, c) |
503 cleaned = append(cleaned, c) |
502 default: |
504 default: |
503 before = true |
505 before = true |
504 cleaned = append(cleaned, c) |
506 cleaned = append(cleaned, c) |