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}