whatwg_datetime/components/date.rs
1use crate::tokens::TOKEN_HYPHEN;
2use crate::{collect_day_and_validate, parse_format, parse_month_component};
3use chrono::NaiveDate;
4
5/// Parse a [proleptic-Gregorian date][proleptic-greg], in the format of `YYYY-MM-DD`
6///
7/// This follows the rules for [parsing a date string][whatwg-html-parse]
8/// per [WHATWG HTML Standard § 2.3.5.2 Dates][whatwg-html-dates].
9///
10/// # Examples
11/// ```
12/// use chrono::NaiveDate;
13/// use whatwg_datetime::parse_date;
14///
15/// assert_eq!(parse_date("2011-11-18"), NaiveDate::from_ymd_opt(2011, 11, 18));
16/// assert_eq!(parse_date("2012-02-29"), NaiveDate::from_ymd_opt(2012, 2, 29));
17/// assert_eq!(parse_date("2007-02-29"), None); // 2007 is not a leap year
18/// assert_eq!(parse_date("2011-00-19"), None); // invalid month
19/// assert_eq!(parse_date("2012-11-1"), None); // invalid day length, must be 2 digits/zero-padded
20/// assert_eq!(parse_date("0000-11-02"), None); // invalid year, must be at least 0001
21/// ```
22///
23/// [proleptic-greg]: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#proleptic-gregorian-date
24/// [whatwg-html-dates]: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#dates
25/// [whatwg-html-parse]: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-date-string
26#[inline]
27pub fn parse_date(s: &str) -> Option<NaiveDate> {
28 parse_format(s, parse_date_component)
29}
30
31/// Low-level function for parsing an individual date component at a given position
32///
33/// This follows the rules for [parsing a date component][whatwg-html-parse],
34/// per [WHATWG HTML Standard § 2.3.5.2 Dates][whatwg-html-dates].
35///
36/// > **Note**:
37/// > This function exposes a lower-level API than [`parse_date`]. More than likely,
38/// > you will want to use [`parse_date`] instead.
39///
40/// # Examples
41/// ```
42/// use chrono::NaiveDate;
43/// use whatwg_datetime::parse_date_component;
44///
45/// let mut position = 0usize;
46/// let date = parse_date_component("2011-11-18", &mut position);
47///
48/// assert_eq!(date, NaiveDate::from_ymd_opt(2011, 11, 18));
49/// ```
50///
51/// [whatwg-html-dates]: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#dates
52/// [whatwg-html-parse]: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-date-component
53pub fn parse_date_component(s: &str, position: &mut usize) -> Option<NaiveDate> {
54 let year_month = parse_month_component(s, position)?;
55 let year = year_month.year;
56 let month = year_month.month;
57
58 if *position > s.len() || s.chars().nth(*position) != Some(TOKEN_HYPHEN) {
59 return None;
60 } else {
61 *position += 1;
62 }
63
64 let day = collect_day_and_validate(s, position, month)?;
65 NaiveDate::from_ymd_opt(year, month, day)
66}
67
68#[cfg(test)]
69mod tests {
70 use super::parse_date;
71 use chrono::NaiveDate;
72
73 #[test]
74 fn test_parse_date() {
75 assert_eq!(
76 parse_date("2011-11-18"),
77 NaiveDate::from_ymd_opt(2011, 11, 18)
78 );
79 }
80
81 #[test]
82 fn test_parse_date_leap_year() {
83 assert_eq!(
84 parse_date("2012-02-29"),
85 NaiveDate::from_ymd_opt(2012, 2, 29)
86 );
87 }
88
89 #[test]
90 fn test_parse_date_fails_not_leap_year() {
91 assert_eq!(parse_date("2007-02-29"), None);
92 }
93
94 #[test]
95 fn test_parse_date_fails_invalid_month() {
96 assert_eq!(parse_date("2011-00-19"), None);
97 }
98
99 #[test]
100 fn test_parse_date_fails_invalid_day_length() {
101 assert_eq!(parse_date("2011-11-0"), None);
102 }
103
104 #[test]
105 fn test_parse_date_fails_invalid_day_upper_bound() {
106 assert_eq!(parse_date("2011-11-32"), None);
107 }
108
109 #[test]
110 fn test_parse_date_fails_invalid_separator() {
111 assert_eq!(parse_date("2011-11/19"), None);
112 }
113}