some date parsing magic

This commit is contained in:
Abraham Toriz 2021-07-02 10:07:07 -05:00
parent 4980b3df67
commit a56df91b58
No known key found for this signature in database
GPG Key ID: D5B4A746DB5DD42A
3 changed files with 75 additions and 13 deletions

View File

@ -22,7 +22,8 @@ fn local_to_utc(t: DateTime<Utc>) -> error::Result<DateTime<Utc>> {
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(),
}),
};

View File

@ -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<Local>,
t2: DateTime<Local>,
t1: NaiveDateTime,
t2: NaiveDateTime,
}
}

View File

@ -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<DateTime<Utc>> {
(?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
(?P<offset>
(?P<utc>Z)|((?P<sign>\+|-)(?P<ohour>\d{1,2}):(?P<omin>\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::<i32>().unwrap() * 60 * 60;
offset += (&caps["omin"]).parse::<i32>().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<DateTime<Utc>> {
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]