Allow out-of-bounds values for Time

This commit is contained in:
Joscha 2021-12-11 22:39:20 +01:00
parent 44bcd5712d
commit 6e6a696ca4
3 changed files with 46 additions and 21 deletions

View file

@ -358,7 +358,7 @@ mod tests {
fn apply_dt(step: Step, from: (i32, u32, u32, u32, u32)) -> Result<(NaiveDate, Time)> { fn apply_dt(step: Step, from: (i32, u32, u32, u32, u32)) -> Result<(NaiveDate, Time)> {
delta(step).apply_date_time( delta(step).apply_date_time(
NaiveDate::from_ymd(from.0, from.1, from.2), 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(), apply_dt(step, from).unwrap(),
( (
NaiveDate::from_ymd(expected.0, expected.1, expected.2), 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] #[test]
fn delta_time() { fn delta_time() {
test_dt( test_dt(
Step::Time(Time::new(12, 33).unwrap()), Step::Time(Time::new(12, 33)),
(2021, 7, 3, 12, 34), (2021, 7, 3, 12, 34),
(2021, 7, 4, 12, 33), (2021, 7, 4, 12, 33),
); );
test_dt( 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),
(2021, 7, 3, 12, 34), (2021, 7, 3, 12, 34),
); );
test_dt( test_dt(
Step::Time(Time::new(12, 35).unwrap()), Step::Time(Time::new(12, 35)),
(2021, 7, 3, 12, 34), (2021, 7, 3, 12, 34),
(2021, 7, 3, 12, 35), (2021, 7, 3, 12, 35),
); );
// 24:00 != 00:00 // 24:00 != 00:00
test_dt( test_dt(
Step::Time(Time::new(24, 0).unwrap()), Step::Time(Time::new(24, 0)),
(2021, 7, 3, 12, 0), (2021, 7, 3, 12, 0),
(2021, 7, 3, 24, 0), (2021, 7, 3, 24, 0),
); );
test_dt( test_dt(
Step::Time(Time::new(0, 0).unwrap()), Step::Time(Time::new(0, 0)),
(2021, 7, 3, 12, 0), (2021, 7, 3, 12, 0),
(2021, 7, 4, 0, 0), (2021, 7, 4, 0, 0),
); );
// Requires time // 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());
} }
} }

View file

@ -84,9 +84,11 @@ fn parse_time(p: Pair<'_, Rule>) -> Result<Spanned<Time>> {
assert_eq!(p.next(), None); assert_eq!(p.next(), None);
match Time::new(hour, min) { let time = Time::new(hour, min);
Some(time) => Ok(Spanned::new(span, time)), if time.in_normal_range() {
None => fail(pspan, "invalid time"), Ok(Spanned::new(span, time))
} else {
fail(pspan, "invalid time")
} }
} }

View file

@ -1,6 +1,8 @@
use std::cmp::{self, Ordering}; use std::cmp::{self, Ordering};
use std::fmt; use std::fmt;
use chrono::{NaiveTime, Timelike};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Span { pub struct Span {
pub start: usize, pub start: usize,
@ -63,18 +65,39 @@ impl fmt::Debug for Time {
} }
} }
impl From<NaiveTime> for Time {
fn from(t: NaiveTime) -> Self {
Self::new(t.hour(), t.minute())
}
}
impl Time { impl Time {
pub fn new(hour: u32, min: u32) -> Option<Self> { pub fn new(hour: u32, min: u32) -> Self {
if hour < 24 && min < 60 || hour == 24 && min == 0 { Self {
Some(Self { hour: hour as u8,
hour: hour as u8, min: min as u8,
min: min as u8,
})
} else {
None
} }
} }
/// 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) { pub fn add_minutes(&self, amount: i32) -> (i32, Self) {
match amount.cmp(&0) { match amount.cmp(&0) {
Ordering::Less => { Ordering::Less => {
@ -85,7 +108,7 @@ impl Time {
let hour = mins.div_euclid(60) as u32; let hour = mins.div_euclid(60) as u32;
let min = mins.rem_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 => { Ordering::Greater => {
let mut mins = (self.hour as i32) * 60 + (self.min as i32) + amount; 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 hour = mins.div_euclid(60) as u32;
let min = mins.rem_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), Ordering::Equal => (0, *self),
} }