diff --git a/src/commands/display.rs b/src/commands/display.rs index 270df3a..03de1cf 100644 --- a/src/commands/display.rs +++ b/src/commands/display.rs @@ -22,7 +22,8 @@ fn local_to_utc(t: DateTime) -> error::Result> { LocalResult::Single(t) => t, LocalResult::Ambiguous(t1, t2) => return Err(error::Error::AmbiguousLocalTime { orig: t.naive_utc().to_string(), - t1, t2, + t1: t1.naive_local(), + t2: t2.naive_local(), }), }; diff --git a/src/error.rs b/src/error.rs index f9ca9c5..d9bfd14 100644 --- a/src/error.rs +++ b/src/error.rs @@ -45,8 +45,8 @@ pub enum Error { #[error("Trying to parse {orig} as a time in your timezone led to the ambiguous results {t1} and {t2}")] AmbiguousLocalTime { orig: String, - t1: DateTime, - t2: DateTime, + t1: NaiveDateTime, + t2: NaiveDateTime, } } diff --git a/src/timeparse.rs b/src/timeparse.rs index f5075af..71cebff 100644 --- a/src/timeparse.rs +++ b/src/timeparse.rs @@ -1,4 +1,4 @@ -use chrono::{DateTime, Utc, Local, TimeZone, LocalResult}; +use chrono::{DateTime, Utc, Local, TimeZone, LocalResult, FixedOffset}; use regex::Regex; use crate::error::{Result, Error}; @@ -17,18 +17,64 @@ pub fn parse_time(input: &str) -> Result> { (?P\d{2})? # the minute, optional (. # a separator (?P\d{2}))?)?)? # the second, optional, implies minute - (?P(?PZ)|(?P(\+|-)\d{2}:\d{2}))? # the offset, optional + (?P + (?PZ)|((?P\+|-)(?P\d{1,2}):(?P\d{2})) + )? # the offset, optional ").unwrap(); if let Some(caps) = re.captures(input) { - if let Some(_) = caps.name("offset") { + return if let Some(_) = caps.name("offset") { // start with a specific offset or utc if let Some(_) = caps.name("utc") { // start with utc - unimplemented!() + let try_date = Utc.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), + ); + + match try_date { + LocalResult::None => Err(Error::NoneLocalTime(input.into())), + LocalResult::Single(t) => Ok(t), + LocalResult::Ambiguous(t1, t2) => Err(Error::AmbiguousLocalTime { + orig: input.into(), + t1: t1.naive_utc(), + t2: t2.naive_utc(), + }), + } } else { - // start with an offset - unimplemented!() + let mut offset: i32 = (&caps["ohour"]).parse::().unwrap() * 60 * 60; + offset += (&caps["omin"]).parse::().unwrap() * 60; + + let fo = if &caps["sign"] == "+" { + FixedOffset::east(offset) + } else { + FixedOffset::west(offset) + }; + + let try_date = fo.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), + ); + + 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: t1.naive_utc(), + t2: t2.naive_utc(), + }), + } } } else { // start with a local time @@ -42,15 +88,16 @@ pub fn parse_time(input: &str) -> Result> { caps.name("second").map(|m| m.as_str().parse().unwrap()).unwrap_or(0), ); - return match try_date { + 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, + t1: t1.naive_utc(), + t2: t2.naive_utc(), }), - }; - } + } + }; } Err(Error::DateTimeParseError(input.into())) @@ -88,10 +135,24 @@ mod tests { assert_eq!(parse_time("11:36:35").unwrap(), now.date().and_hms(16, 36, 35)); } + #[test] + fn parse_hour_with_timezone() { + std::env::set_var("TZ", "America/Mexico_City"); + + let now = Utc::now(); + + assert_eq!(parse_time("11:36Z").unwrap(), now.date().and_hms(11, 36, 0)); + assert_eq!(parse_time("11:36:35z").unwrap(), now.date().and_hms(11, 36, 35)); + + assert_eq!(parse_time("11:36-5:00").unwrap(), now.date().and_hms(16, 36, 0)); + assert_eq!(parse_time("11:36:35+5:00").unwrap(), now.date().and_hms(6, 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)); + assert_eq!(parse_time("2021-05-21T11:36:12+3:00").unwrap(), Utc.ymd(2021, 5, 21).and_hms(8, 36, 12)); } #[test]