time_util/
parse.rs

1// Copyright (C) 2020-2021 Daniel Mueller <deso@posteo.net>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use std::time::SystemTime;
5
6use chrono::naive::NaiveDate;
7use chrono::offset::FixedOffset;
8use chrono::offset::TimeZone as _;
9use chrono::DateTime;
10use chrono::ParseError;
11
12
13type DateFn = fn(&str) -> Result<DateTime<FixedOffset>, ParseError>;
14
15
16/// The list of time stamp formats we support.
17pub(crate) const TIME_PARSE_FNS: [DateFn; 3] = [
18  |s| FixedOffset::east(0).datetime_from_str(s, "%Y-%m-%dT%H:%M:%S%.fZ"),
19  |s| FixedOffset::east(0).datetime_from_str(s, "%Y-%m-%dT%H:%M:%SZ"),
20  |s| DateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f%z"),
21];
22
23pub(crate) const DATE_PARSE_FNS: [DateFn; 1] = [|s| {
24  NaiveDate::parse_from_str(s, "%Y-%m-%d")
25    .map(|date| DateTime::from_utc(date.and_hms(0, 0, 0), FixedOffset::east(0)))
26}];
27
28
29/// Parse a `SystemTime` from a string using any of the provided parsing
30/// functions.
31pub(crate) fn parse_system_time_from_str_impl(
32  time: &str,
33  parse_fns: &[DateFn],
34) -> Option<SystemTime> {
35  for parse_fn in parse_fns {
36    // Ideally we would want to only continue in case of
37    // ParseErrorKind::Invalid. However, that member is private...
38    let datetime = match parse_fn(&time) {
39      Ok(datetime) => datetime,
40      Err(_) => continue,
41    };
42    return Some(SystemTime::from(
43      datetime.with_timezone(&FixedOffset::east(0)),
44    ))
45  }
46  None
47}
48
49
50/// Parse a `SystemTime` from a string.
51pub fn parse_system_time_from_str(time: &str) -> Option<SystemTime> {
52  parse_system_time_from_str_impl(&time, &TIME_PARSE_FNS)
53}
54
55
56/// Parse a `SystemTime` from a date string.
57pub fn parse_system_time_from_date_str(time: &str) -> Option<SystemTime> {
58  parse_system_time_from_str_impl(&time, &DATE_PARSE_FNS)
59}
60
61
62#[cfg(test)]
63mod tests {
64  use super::*;
65
66  use std::time::Duration;
67  use std::time::UNIX_EPOCH;
68
69
70  #[test]
71  fn parse_time() {
72    let time = parse_system_time_from_str("2018-04-01T12:00:00.000Z").unwrap();
73    let expected = UNIX_EPOCH + Duration::from_secs(1_522_584_000);
74    assert_eq!(time, expected)
75  }
76
77  /// Check that parsing an unexpected time fails.
78  #[test]
79  fn parse_time_fail() {
80    let result = parse_system_time_from_str("2018-04-01TXX:00:00.000");
81    assert_eq!(result, None)
82  }
83
84  #[test]
85  fn parse_date() {
86    let time = parse_system_time_from_date_str("2019-08-01").unwrap();
87    let expected = UNIX_EPOCH + Duration::from_secs(1_564_617_600);
88    assert_eq!(time, expected)
89  }
90}