(string: &str)
| 612 | } |
| 613 | |
| 614 | fn parse_date(string: &str) -> Option<NaiveDate> { |
| 615 | // If the date has an extended (signed) year such as "+10999-12-31" or "-0012-05-06" |
| 616 | // |
| 617 | // According to [ISO 8601], years have: |
| 618 | // Four digits or more for the year. Years in the range 0000 to 9999 will be pre-padded by |
| 619 | // zero to ensure four digits. Years outside that range will have a prefixed positive or negative symbol. |
| 620 | // |
| 621 | // [ISO 8601]: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/format/DateTimeFormatter.html#ISO_LOCAL_DATE |
| 622 | if string.starts_with('+') || string.starts_with('-') { |
| 623 | let (year, month, day) = parse_extended_ymd(string)?; |
| 624 | return NaiveDate::from_ymd_opt(year, month, day); |
| 625 | } |
| 626 | |
| 627 | if string.len() > 10 { |
| 628 | // Try to parse as datetime and return just the date part |
| 629 | return string_to_datetime(&Utc, string) |
| 630 | .map(|dt| dt.date_naive()) |
| 631 | .ok(); |
| 632 | }; |
| 633 | let mut digits = [0; 10]; |
| 634 | let mut mask = 0; |
| 635 | |
| 636 | // Treating all bytes the same way, helps LLVM vectorise this correctly |
| 637 | for (idx, (o, i)) in digits.iter_mut().zip(string.bytes()).enumerate() { |
| 638 | *o = i.wrapping_sub(b'0'); |
| 639 | mask |= ((*o < 10) as u16) << idx |
| 640 | } |
| 641 | |
| 642 | const HYPHEN: u8 = b'-'.wrapping_sub(b'0'); |
| 643 | |
| 644 | // refer to https://www.rfc-editor.org/rfc/rfc3339#section-3 |
| 645 | if digits[4] != HYPHEN { |
| 646 | let (year, month, day) = match (mask, string.len()) { |
| 647 | (0b11111111, 8) => ( |
| 648 | digits[0] as u16 * 1000 |
| 649 | + digits[1] as u16 * 100 |
| 650 | + digits[2] as u16 * 10 |
| 651 | + digits[3] as u16, |
| 652 | digits[4] * 10 + digits[5], |
| 653 | digits[6] * 10 + digits[7], |
| 654 | ), |
| 655 | _ => return None, |
| 656 | }; |
| 657 | return NaiveDate::from_ymd_opt(year as _, month as _, day as _); |
| 658 | } |
| 659 | |
| 660 | let (month, day) = match mask { |
| 661 | 0b1101101111 => { |
| 662 | if digits[7] != HYPHEN { |
| 663 | return None; |
| 664 | } |
| 665 | (digits[5] * 10 + digits[6], digits[8] * 10 + digits[9]) |
| 666 | } |
| 667 | 0b101101111 => { |
| 668 | if digits[7] != HYPHEN { |
| 669 | return None; |
| 670 | } |
| 671 | (digits[5] * 10 + digits[6], digits[8]) |
no test coverage detected