whatwg_datetime/components/
local_datetime.rs

1use crate::tokens::{TOKEN_SPACE, TOKEN_T};
2use crate::{parse_date_component, parse_time_component};
3use chrono::NaiveDateTime;
4
5/// Parse a [proleptic-Gregorian date][proleptic-greg] consisting
6/// of a date, time, with no time-zone information
7///
8/// This follows the rules for [parsing a local datetime string][whatwg-html-parse]
9/// per [WHATWG HTML Standard ยง 2.3.5.5 Local dates and times][whatwg-html-local-datetime].
10///
11/// # Examples
12/// ```
13/// use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
14/// use whatwg_datetime::parse_local_datetime;
15///
16/// // Parse a local datetime string with a date,
17/// // a T delimiter, anda  time with fractional seconds
18/// assert_eq!(
19///     parse_local_datetime("2011-11-18T14:54:39.929"),
20///     Some(NaiveDateTime::new(
21///         NaiveDate::from_ymd_opt(2011, 11, 18).unwrap(),
22///         NaiveTime::from_hms_milli_opt(14, 54, 39, 929).unwrap(),
23///     ))
24/// );
25/// ```
26///
27/// [proleptic-greg]: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#proleptic-gregorian-date
28/// [whatwg-html-local-datetime]: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#local-dates-and-times
29/// [whatwg-html-parse]: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-local-date-and-time-string
30pub fn parse_local_datetime(s: &str) -> Option<NaiveDateTime> {
31	let mut position = 0usize;
32	let date = parse_date_component(s, &mut position)?;
33
34	let last_char = s.chars().nth(position);
35	if position > s.len() || !matches!(last_char, Some(TOKEN_T) | Some(TOKEN_SPACE)) {
36		return None;
37	} else {
38		position += 1;
39	}
40
41	let time = parse_time_component(s, &mut position)?;
42	if position < s.len() {
43		return None;
44	}
45
46	Some(NaiveDateTime::new(date, time))
47}
48
49#[cfg(test)]
50mod tests {
51	use super::parse_local_datetime;
52	use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
53
54	#[test]
55	pub fn test_parse_local_datetime_delimited_t_date_hm() {
56		assert_eq!(
57			parse_local_datetime("2004-12-31T12:31"),
58			Some(NaiveDateTime::new(
59				NaiveDate::from_ymd_opt(2004, 12, 31).unwrap(),
60				NaiveTime::from_hms_opt(12, 31, 0).unwrap(),
61			))
62		);
63	}
64
65	#[test]
66	pub fn test_parse_local_datetime_delimited_t_date_hms() {
67		assert_eq!(
68			parse_local_datetime("2004-12-31T12:31:59"),
69			Some(NaiveDateTime::new(
70				NaiveDate::from_ymd_opt(2004, 12, 31).unwrap(),
71				NaiveTime::from_hms_opt(12, 31, 59).unwrap(),
72			))
73		);
74	}
75
76	#[test]
77	pub fn test_parse_local_datetime_delimited_t_date_hms_milliseconds() {
78		assert_eq!(
79			parse_local_datetime("2011-11-18T14:54:39.929"),
80			Some(NaiveDateTime::new(
81				NaiveDate::from_ymd_opt(2011, 11, 18).unwrap(),
82				NaiveTime::from_hms_milli_opt(14, 54, 39, 929).unwrap(),
83			))
84		)
85	}
86
87	#[test]
88	pub fn test_parse_local_datetime_delimited_space_date_hm() {
89		assert_eq!(
90			parse_local_datetime("2011-11-18 14:54"),
91			Some(NaiveDateTime::new(
92				NaiveDate::from_ymd_opt(2011, 11, 18).unwrap(),
93				NaiveTime::from_hms_opt(14, 54, 0).unwrap(),
94			))
95		)
96	}
97
98	#[test]
99	pub fn test_parse_local_datetime_delimited_space_date_hms() {
100		assert_eq!(
101			parse_local_datetime("2011-11-18 14:54:39"),
102			Some(NaiveDateTime::new(
103				NaiveDate::from_ymd_opt(2011, 11, 18).unwrap(),
104				NaiveTime::from_hms_opt(14, 54, 39).unwrap(),
105			))
106		)
107	}
108
109	#[test]
110	pub fn test_parse_local_datetime_delimited_space_date_hms_milliseconds() {
111		assert_eq!(
112			parse_local_datetime("2011-11-18 14:54:39.929"),
113			Some(NaiveDateTime::new(
114				NaiveDate::from_ymd_opt(2011, 11, 18).unwrap(),
115				NaiveTime::from_hms_milli_opt(14, 54, 39, 929).unwrap(),
116			))
117		)
118	}
119
120	#[test]
121	pub fn test_parse_local_datetime_fails_invalid_delimiter() {
122		assert_eq!(parse_local_datetime("2011-11-18W14-54-39"), None);
123	}
124
125	#[test]
126	pub fn test_parse_local_datetime_fails_invalid_date() {
127		assert_eq!(parse_local_datetime("2011/11/18T14:54:39.929"), None);
128	}
129
130	#[test]
131	pub fn test_parse_local_datetime_fails_invalid_time() {
132		assert_eq!(parse_local_datetime("2011-11-18T14/54/39"), None);
133	}
134}