116 lines
4.6 KiB
Rust
116 lines
4.6 KiB
Rust
|
use chrono::{DateTime, Utc, Local, TimeZone, LocalResult};
|
||
|
use regex::Regex;
|
||
|
|
||
|
use crate::error::{Result, Error};
|
||
|
|
||
|
pub fn parse_time(input: &str) -> Result<DateTime<Utc>> {
|
||
|
// first try to parse as a full datetime with optional timezone
|
||
|
let re = Regex::new(r"(?xi)
|
||
|
(?P<year>\d{4}) # the year, mandatory
|
||
|
.
|
||
|
(?P<month>\d{2}) # the month, mandatory
|
||
|
.
|
||
|
(?P<day>\d{2}) # the day, mandatory
|
||
|
(. # a separator
|
||
|
(?P<hour>\d{2}) # the hour, optional
|
||
|
(. # a separator
|
||
|
(?P<minute>\d{2})? # the minute, optional
|
||
|
(. # a separator
|
||
|
(?P<second>\d{2}))?)?)? # the second, optional, implies minute
|
||
|
(?P<offset>(?P<utc>Z)|(?P<fixedoffset>(\+|-)\d{2}:\d{2}))? # the offset, optional
|
||
|
").unwrap();
|
||
|
|
||
|
if let Some(caps) = re.captures(input) {
|
||
|
if let Some(_) = caps.name("offset") {
|
||
|
// start with a specific offset or utc
|
||
|
if let Some(_) = caps.name("utc") {
|
||
|
// start with utc
|
||
|
unimplemented!()
|
||
|
} else {
|
||
|
// start with an offset
|
||
|
unimplemented!()
|
||
|
}
|
||
|
} else {
|
||
|
// start with a local time
|
||
|
let try_date = Local.ymd_opt(
|
||
|
(&caps["year"]).parse().unwrap(),
|
||
|
(&caps["month"]).parse().unwrap(),
|
||
|
(&caps["day"]).parse().unwrap(),
|
||
|
).and_hms_opt(
|
||
|
caps.name("hour").map(|m| m.as_str().parse().unwrap()).unwrap_or(0),
|
||
|
caps.name("minute").map(|m| m.as_str().parse().unwrap()).unwrap_or(0),
|
||
|
caps.name("second").map(|m| m.as_str().parse().unwrap()).unwrap_or(0),
|
||
|
);
|
||
|
|
||
|
return match try_date {
|
||
|
LocalResult::None => Err(Error::NoneLocalTime(input.into())),
|
||
|
LocalResult::Single(t) => Ok(Utc.from_utc_datetime(&t.naive_utc())),
|
||
|
LocalResult::Ambiguous(t1, t2) => Err(Error::AmbiguousLocalTime {
|
||
|
orig: input.into(),
|
||
|
t1, t2,
|
||
|
}),
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Err(Error::DateTimeParseError(input.into()))
|
||
|
}
|
||
|
|
||
|
#[cfg(test)]
|
||
|
mod tests {
|
||
|
use chrono::TimeZone;
|
||
|
|
||
|
use super::*;
|
||
|
|
||
|
#[test]
|
||
|
fn parse_datetime_string() {
|
||
|
std::env::set_var("TZ", "America/Mexico_City");
|
||
|
|
||
|
assert_eq!(parse_time("2021-05-21 11:36").unwrap(), Utc.ymd(2021, 5, 21).and_hms(16, 36, 0));
|
||
|
assert_eq!(parse_time("2021-05-21 11:36:12").unwrap(), Utc.ymd(2021, 5, 21).and_hms(16, 36, 12));
|
||
|
|
||
|
assert_eq!(parse_time("2021-05-21T11:36").unwrap(), Utc.ymd(2021, 5, 21).and_hms(16, 36, 0));
|
||
|
assert_eq!(parse_time("2021-05-21T11:36:12").unwrap(), Utc.ymd(2021, 5, 21).and_hms(16, 36, 12));
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn parse_date() {
|
||
|
assert_eq!(parse_time("2021-05-21").unwrap(), Utc.ymd(2021, 5, 21).and_hms(5, 0, 0));
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn parse_hour() {
|
||
|
std::env::set_var("TZ", "America/Mexico_City");
|
||
|
|
||
|
let now = Utc::now();
|
||
|
|
||
|
assert_eq!(parse_time("11:36").unwrap(), now.date().and_hms(16, 36, 0));
|
||
|
assert_eq!(parse_time("11:36:35").unwrap(), now.date().and_hms(16, 36, 35));
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn parse_with_specified_timezone() {
|
||
|
assert_eq!(parse_time("2021-05-21T11:36:12Z").unwrap(), Utc.ymd(2021, 5, 21).and_hms(11, 36, 12));
|
||
|
assert_eq!(parse_time("2021-05-21T11:36:12-3:00").unwrap(), Utc.ymd(2021, 5, 21).and_hms(14, 36, 12));
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn parse_human_time() {
|
||
|
assert_eq!(parse_time("an hour ago").unwrap(), Utc.ymd(2021, 5, 21).and_hms(11, 36, 12));
|
||
|
assert_eq!(parse_time("two hours ago").unwrap(), Utc.ymd(2021, 5, 21).and_hms(11, 36, 12));
|
||
|
assert_eq!(parse_time("4 hours ago").unwrap(), Utc.ymd(2021, 5, 21).and_hms(11, 36, 12));
|
||
|
|
||
|
assert_eq!(parse_time("a minute ago").unwrap(), Utc.ymd(2021, 5, 21).and_hms(11, 36, 12));
|
||
|
assert_eq!(parse_time("two minutes ago").unwrap(), Utc.ymd(2021, 5, 21).and_hms(11, 36, 12));
|
||
|
assert_eq!(parse_time("4 minutes ago").unwrap(), Utc.ymd(2021, 5, 21).and_hms(11, 36, 12));
|
||
|
|
||
|
assert_eq!(parse_time("hace una hora").unwrap(), Utc.ymd(2021, 5, 21).and_hms(11, 36, 12));
|
||
|
assert_eq!(parse_time("hace dos horas").unwrap(), Utc.ymd(2021, 5, 21).and_hms(11, 36, 12));
|
||
|
assert_eq!(parse_time("hace cuatro horas").unwrap(), Utc.ymd(2021, 5, 21).and_hms(11, 36, 12));
|
||
|
|
||
|
assert_eq!(parse_time("hace una minuto").unwrap(), Utc.ymd(2021, 5, 21).and_hms(11, 36, 12));
|
||
|
assert_eq!(parse_time("hace dos minutos").unwrap(), Utc.ymd(2021, 5, 21).and_hms(11, 36, 12));
|
||
|
assert_eq!(parse_time("hace cuatro minutos").unwrap(), Utc.ymd(2021, 5, 21).and_hms(11, 36, 12));
|
||
|
}
|
||
|
}
|