use chrono::{offset::Local as LocalTime, NaiveDateTime, NaiveTime};
use std::time::Duration;
#[derive(Debug)]
pub enum TimePoint {
Duration(Duration),
Time(NaiveTime),
}
impl TimePoint {
#[must_use]
pub fn remaining_from_now(&self) -> Duration {
let now = LocalTime::now().naive_local();
self.remaining_from(now)
}
#[must_use]
pub fn remaining_from(&self, now: NaiveDateTime) -> Duration {
match self {
TimePoint::Duration(dur) => *dur,
TimePoint::Time(time) => {
let remaining_time = *time - now.time();
match remaining_time.to_std() {
Ok(dur) => dur,
Err(_) => (remaining_time + chrono::Duration::days(1))
.to_std()
.expect(
"should point to the future since we added a day to the current day",
),
}
}
}
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use chrono::NaiveDate;
use once_cell::sync::Lazy;
use super::*;
#[allow(clippy::identity_op)]
const HOUR: Duration = Duration::from_secs(
1 * 60 * 60,
);
static NOW: Lazy<NaiveDateTime> = Lazy::new(|| {
NaiveDateTime::new(
NaiveDate::from_ymd_opt(2000, 1, 1).unwrap(),
NaiveTime::from_hms_opt(12, 0, 0).unwrap(),
)
});
#[test]
fn duration() {
let time_point = TimePoint::Duration(HOUR * 5);
assert_eq!(time_point.remaining_from(*NOW), HOUR * 5);
}
#[test]
fn time_not_yet_passed() {
let at_2_pm = TimePoint::Time(NaiveTime::from_hms_opt(14, 0, 0).unwrap());
assert_eq!(at_2_pm.remaining_from(*NOW), HOUR * 2);
}
#[test]
fn time_already_passed() {
let at_10_am = TimePoint::Time(NaiveTime::from_hms_opt(10, 0, 0).unwrap());
assert_eq!(at_10_am.remaining_from(*NOW), HOUR * 22);
}
}