use time::{
format_description::well_known::{Rfc2822, Rfc3339},
Date, OffsetDateTime, PrimitiveDateTime, Time,
};
pub fn parse_timestamp_or_relative_time(s: &str) -> Result<OffsetDateTime, anyhow::Error> {
parse_timestamp_or_relative_time_based(s, OffsetDateTime::now_utc())
}
pub fn parse_timestamp_or_relative_time_based(
s: &str,
base: OffsetDateTime,
) -> Result<OffsetDateTime, anyhow::Error> {
if let Ok(t) = OffsetDateTime::parse(s, &Rfc3339) {
return Ok(t);
}
if let Ok(t) = OffsetDateTime::parse(s, &Rfc2822) {
return Ok(t);
}
if let Ok(t) = Date::parse(s, time::macros::format_description!("[year]-[month]-[day]")) {
return Ok(PrimitiveDateTime::new(t, Time::MIDNIGHT).assume_utc());
}
if let Ok(t) = OffsetDateTime::parse(s, time::macros::format_description!("[unix_timestamp]")) {
return Ok(t);
}
let (is_negative, v) = match s.strip_prefix('-') {
Some(rest) => (true, rest),
None => (false, s.trim_start_matches('+')),
};
if let Ok(duration) = v.parse::<super::PrettyDuration>() {
let time = if is_negative {
base - duration.0
} else {
base + duration.0
};
return Ok(time);
}
anyhow::bail!("Unable to parse the timestamp - no known format matched")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_reltime() {
let expected = time::macros::datetime!(2006-01-02 03:04:05 -07:00);
assert_eq!(
parse_timestamp_or_relative_time("2006-01-02T03:04:05-07:00").unwrap(),
expected,
);
assert_eq!(
parse_timestamp_or_relative_time("2006-01-02T10:04:05Z").unwrap(),
expected
);
assert_eq!(
parse_timestamp_or_relative_time("2006-01-02T10:04:05.000000000Z").unwrap(),
expected
);
assert_eq!(
parse_timestamp_or_relative_time("Mon, 02 Jan 2006 03:04:05 MST").unwrap(),
expected,
);
assert_eq!(
parse_timestamp_or_relative_time("2006-01-02").unwrap(),
time::macros::datetime!(2006-01-02 00:00:00 +00:00),
);
assert_eq!(
parse_timestamp_or_relative_time("1136196245").unwrap(),
expected
);
}
}