diff --git a/src/eval/delta.rs b/src/eval/delta.rs index 08402e4..883202f 100644 --- a/src/eval/delta.rs +++ b/src/eval/delta.rs @@ -358,7 +358,7 @@ mod tests { fn apply_dt(step: Step, from: (i32, u32, u32, u32, u32)) -> Result<(NaiveDate, Time)> { delta(step).apply_date_time( NaiveDate::from_ymd(from.0, from.1, from.2), - Time::new(from.3, from.4).unwrap(), + Time::new(from.3, from.4), ) } @@ -368,7 +368,7 @@ mod tests { apply_dt(step, from).unwrap(), ( NaiveDate::from_ymd(expected.0, expected.1, expected.2), - Time::new(expected.3, expected.4).unwrap() + Time::new(expected.3, expected.4) ) ); } @@ -552,34 +552,34 @@ mod tests { #[test] fn delta_time() { test_dt( - Step::Time(Time::new(12, 33).unwrap()), + Step::Time(Time::new(12, 33)), (2021, 7, 3, 12, 34), (2021, 7, 4, 12, 33), ); test_dt( - Step::Time(Time::new(12, 34).unwrap()), + Step::Time(Time::new(12, 34)), (2021, 7, 3, 12, 34), (2021, 7, 3, 12, 34), ); test_dt( - Step::Time(Time::new(12, 35).unwrap()), + Step::Time(Time::new(12, 35)), (2021, 7, 3, 12, 34), (2021, 7, 3, 12, 35), ); // 24:00 != 00:00 test_dt( - Step::Time(Time::new(24, 0).unwrap()), + Step::Time(Time::new(24, 0)), (2021, 7, 3, 12, 0), (2021, 7, 3, 24, 0), ); test_dt( - Step::Time(Time::new(0, 0).unwrap()), + Step::Time(Time::new(0, 0)), (2021, 7, 3, 12, 0), (2021, 7, 4, 0, 0), ); // Requires time - assert!(apply_d(Step::Time(Time::new(12, 34).unwrap()), (2021, 7, 3)).is_err()); + assert!(apply_d(Step::Time(Time::new(12, 34)), (2021, 7, 3)).is_err()); } } diff --git a/src/files/parse.rs b/src/files/parse.rs index 50fd490..729f616 100644 --- a/src/files/parse.rs +++ b/src/files/parse.rs @@ -84,9 +84,11 @@ fn parse_time(p: Pair<'_, Rule>) -> Result> { assert_eq!(p.next(), None); - match Time::new(hour, min) { - Some(time) => Ok(Spanned::new(span, time)), - None => fail(pspan, "invalid time"), + let time = Time::new(hour, min); + if time.in_normal_range() { + Ok(Spanned::new(span, time)) + } else { + fail(pspan, "invalid time") } } diff --git a/src/files/primitives.rs b/src/files/primitives.rs index 8052513..80ab28e 100644 --- a/src/files/primitives.rs +++ b/src/files/primitives.rs @@ -1,6 +1,8 @@ use std::cmp::{self, Ordering}; use std::fmt; +use chrono::{NaiveTime, Timelike}; + #[derive(Debug, Clone, Copy)] pub struct Span { pub start: usize, @@ -63,18 +65,39 @@ impl fmt::Debug for Time { } } +impl From for Time { + fn from(t: NaiveTime) -> Self { + Self::new(t.hour(), t.minute()) + } +} + impl Time { - pub fn new(hour: u32, min: u32) -> Option { - if hour < 24 && min < 60 || hour == 24 && min == 0 { - Some(Self { - hour: hour as u8, - min: min as u8, - }) - } else { - None + pub fn new(hour: u32, min: u32) -> Self { + Self { + hour: hour as u8, + min: min as u8, } } + /// Whether this time is within the normal range for times. This means that + /// the minutes are always smaller than 60 and the whole time is between + /// `00:00` and `24:00` (inclusive). + /// + /// In cases like leap seconds or daylight savings time, it is possible that + /// times outside of this range occur. + pub fn in_normal_range(&self) -> bool { + if self.min >= 60 { + return false; + } + if self.hour > 24 { + return false; + } + if self.hour == 24 && self.min != 0 { + return false; + } + true + } + pub fn add_minutes(&self, amount: i32) -> (i32, Self) { match amount.cmp(&0) { Ordering::Less => { @@ -85,7 +108,7 @@ impl Time { let hour = mins.div_euclid(60) as u32; let min = mins.rem_euclid(60) as u32; - (days, Self::new(hour, min).unwrap()) + (days, Self::new(hour, min)) } Ordering::Greater => { let mut mins = (self.hour as i32) * 60 + (self.min as i32) + amount; @@ -101,7 +124,7 @@ impl Time { let hour = mins.div_euclid(60) as u32; let min = mins.rem_euclid(60) as u32; - (days, Self::new(hour, min).unwrap()) + (days, Self::new(hour, min)) } Ordering::Equal => (0, *self), }