use chrono::{TimeDelta, prelude::*}; use chrono_tz::Tz; use std::error::Error; use std::fmt; #[derive(PartialEq, Debug)] pub enum CalendarError { TimezoneConversionFailed(String), } impl fmt::Display for CalendarError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let message = match *self { Self::TimezoneConversionFailed(ref tz) => { format!("Could not convert time to timezone: {}", tz) } }; f.write_str(&message) } } impl Error for CalendarError {} fn create_timetable_vecs(time: &NaiveTime, padding_hrs: i8) -> Vec { let start = -padding_hrs; let end = padding_hrs; (start..=end) .map(|v| time.overflowing_add_signed(TimeDelta::hours(v.into())).0) .collect() } pub fn create_timetable( target_time: &NaiveTime, target_date: &NaiveDate, local_timezone: &Tz, target_timezone: &Tz, padding_hrs: i8, ) -> Result, CalendarError> { let datetime = local_timezone .from_local_datetime(&NaiveDateTime::new( target_date.clone(), target_time.clone(), )) .single() .ok_or_else(|| { CalendarError::TimezoneConversionFailed(local_timezone.name().to_string()) })?; let time_in_timezone = datetime.with_timezone(target_timezone); Ok(create_timetable_vecs(&time_in_timezone.time(), padding_hrs)) } pub fn get_todays_date() -> String { Local::now().format("%d-%m-%Y").to_string() } #[cfg(test)] mod tests { use super::*; use chrono_tz::Europe::Prague; #[test] fn test_timetable_vecs() { assert_eq!( create_timetable_vecs(&NaiveTime::from_hms_opt(10, 0, 0).unwrap(), 4), vec![ NaiveTime::from_hms_opt(06, 0, 0).unwrap(), NaiveTime::from_hms_opt(07, 0, 0).unwrap(), NaiveTime::from_hms_opt(08, 0, 0).unwrap(), NaiveTime::from_hms_opt(09, 0, 0).unwrap(), NaiveTime::from_hms_opt(10, 0, 0).unwrap(), NaiveTime::from_hms_opt(11, 0, 0).unwrap(), NaiveTime::from_hms_opt(12, 0, 0).unwrap(), NaiveTime::from_hms_opt(13, 0, 0).unwrap(), NaiveTime::from_hms_opt(14, 0, 0).unwrap(), ] ); } #[test] fn test_timetable_vecs_overflow() { assert_eq!( create_timetable_vecs(&NaiveTime::from_hms_opt(3, 0, 0).unwrap(), 4), vec![ NaiveTime::from_hms_opt(23, 0, 0).unwrap(), NaiveTime::from_hms_opt(00, 0, 0).unwrap(), NaiveTime::from_hms_opt(01, 0, 0).unwrap(), NaiveTime::from_hms_opt(02, 0, 0).unwrap(), NaiveTime::from_hms_opt(03, 0, 0).unwrap(), NaiveTime::from_hms_opt(04, 0, 0).unwrap(), NaiveTime::from_hms_opt(05, 0, 0).unwrap(), NaiveTime::from_hms_opt(06, 0, 0).unwrap(), NaiveTime::from_hms_opt(07, 0, 0).unwrap(), ] ); } #[test] fn test_timetable_vecs_underflow() { assert_eq!( create_timetable_vecs(&NaiveTime::from_hms_opt(22, 0, 0).unwrap(), 4), vec![ NaiveTime::from_hms_opt(18, 0, 0).unwrap(), NaiveTime::from_hms_opt(19, 0, 0).unwrap(), NaiveTime::from_hms_opt(20, 0, 0).unwrap(), NaiveTime::from_hms_opt(21, 0, 0).unwrap(), NaiveTime::from_hms_opt(22, 0, 0).unwrap(), NaiveTime::from_hms_opt(23, 0, 0).unwrap(), NaiveTime::from_hms_opt(00, 0, 0).unwrap(), NaiveTime::from_hms_opt(01, 0, 0).unwrap(), NaiveTime::from_hms_opt(02, 0, 0).unwrap(), ] ); } #[test] fn test_create_timezone() { assert_eq!( create_timetable( &NaiveTime::from_hms_opt(10, 0, 0).unwrap(), &NaiveDate::from_ymd_opt(2000, 1, 1).unwrap(), &Prague, &Prague, 2 ) .unwrap(), vec![ NaiveTime::from_hms_opt(08, 0, 0).unwrap(), NaiveTime::from_hms_opt(09, 0, 0).unwrap(), NaiveTime::from_hms_opt(10, 0, 0).unwrap(), NaiveTime::from_hms_opt(11, 0, 0).unwrap(), NaiveTime::from_hms_opt(12, 0, 0).unwrap() ] ); } #[test] fn test_create_timezone_nonzero_minutes() { assert_eq!( create_timetable( &NaiveTime::from_hms_opt(10, 30, 0).unwrap(), &NaiveDate::from_ymd_opt(2000, 1, 1).unwrap(), &Prague, &Prague, 2 ) .unwrap(), vec![ NaiveTime::from_hms_opt(08, 30, 0).unwrap(), NaiveTime::from_hms_opt(09, 30, 0).unwrap(), NaiveTime::from_hms_opt(10, 30, 0).unwrap(), NaiveTime::from_hms_opt(11, 30, 0).unwrap(), NaiveTime::from_hms_opt(12, 30, 0).unwrap() ] ); } }