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