tzhelp/src/calendar.rs

177 lines
4.9 KiB
Rust
Raw Normal View History

use chrono::{TimeDelta, prelude::*};
use chrono_tz::Tz;
use std::error::Error;
use std::fmt;
#[derive(PartialEq, Debug)]
enum CalendarError {
UnknownTimezone(String),
WrongTimeFormat,
WrongDateFormat,
TimezoneConversionFailed(String),
}
impl fmt::Display for CalendarError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let message = match *self {
Self::UnknownTimezone(ref tz) => format!("Unknown timezone: {}", tz),
Self::WrongTimeFormat => "Wrong time format".to_string(),
Self::WrongDateFormat => "Wrong date format".to_string(),
Self::TimezoneConversionFailed(ref tz) => {
format!("Could not convert time to timezone: {}", tz)
}
};
f.write_str(&message)
}
}
impl Error for CalendarError {}
fn create_calendar_strings(time: &NaiveTime, padding_hrs: i8) -> Vec<String> {
let start = -padding_hrs;
let end = padding_hrs;
(start..=end)
.map(|v| time.overflowing_add_signed(TimeDelta::hours(v.into())).0)
.map(|h| h.format("%H:%M").to_string())
.collect()
}
pub fn create_timetable(
target_time: &String,
target_date: &String,
in_timezone: &String,
padding_hrs: i8,
) -> Result<Vec<String>, CalendarError> {
let parsed_timezone = in_timezone
.parse::<Tz>()
.map_err(|_| CalendarError::UnknownTimezone(in_timezone.clone()))?;
let parsed_time = if let Ok(hour) = target_time.parse::<u32>() {
NaiveTime::from_hms_opt(hour, 0, 0).ok_or(CalendarError::WrongTimeFormat)?
} else {
NaiveTime::parse_from_str(target_time, "%H:%M")
.map_err(|_| CalendarError::WrongTimeFormat)?
};
let parsed_date = NaiveDate::parse_from_str(target_date, "%d-%m-%Y")
.or_else(|_| NaiveDate::parse_from_str(target_date, "%d-%m"))
.map_err(|_| CalendarError::WrongDateFormat)?;
let datetime = NaiveDateTime::new(parsed_date, parsed_time);
let time_in_timezone = parsed_timezone
.from_local_datetime(&datetime)
.single()
.ok_or(CalendarError::TimezoneConversionFailed(in_timezone.clone()))?;
Ok(create_calendar_strings(
&time_in_timezone.time(),
padding_hrs,
))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_calendar_string() {
assert_eq!(
create_calendar_strings(&NaiveTime::from_hms_opt(10, 0, 0).unwrap(), 4),
vec![
"06:00", "07:00", "08:00", "09:00", "10:00", "11:00", "12:00", "13:00", "14:00"
]
);
}
#[test]
fn test_calendar_string_overflow() {
assert_eq!(
create_calendar_strings(&NaiveTime::from_hms_opt(3, 0, 0).unwrap(), 4),
vec![
"23:00", "00:00", "01:00", "02:00", "03:00", "04:00", "05:00", "06:00", "07:00"
]
);
}
#[test]
fn test_calendar_string_underflow() {
assert_eq!(
create_calendar_strings(&NaiveTime::from_hms_opt(22, 0, 0).unwrap(), 4),
vec![
"18:00", "19:00", "20:00", "21:00", "22:00", "23:00", "00:00", "01:00", "02:00"
]
);
}
#[test]
fn test_create_timezone() {
assert_eq!(
create_timetable(
&"10".to_string(),
&"1-1-2000".to_string(),
&"Europe/Prague".to_string(),
2
)
.unwrap(),
vec!["08:00", "09:00", "10:00", "11:00", "12:00"]
);
}
#[test]
fn test_create_timezone_nonzero_minutes() {
assert_eq!(
create_timetable(
&"10:30".to_string(),
&"1-1-2000".to_string(),
&"Europe/Prague".to_string(),
2
)
.unwrap(),
vec!["08:30", "09:30", "10:30", "11:30", "12:30"]
);
}
#[test]
fn test_create_timezone_wrong_time() {
assert_eq!(
create_timetable(
&"hello".to_string(),
&"1-1-2000".to_string(),
&"Europe/Prague".to_string(),
2,
)
.unwrap_err(),
CalendarError::WrongTimeFormat
);
}
#[test]
fn test_create_timezone_wrong_date() {
assert_eq!(
create_timetable(
&"10:00".to_string(),
&"hello".to_string(),
&"Europe/Prague".to_string(),
2,
)
.unwrap_err(),
CalendarError::WrongDateFormat
);
}
#[test]
fn test_create_timezone_wrong_zone_name() {
assert_eq!(
create_timetable(
&"10:00".to_string(),
&"1-1-2000".to_string(),
&"hello".to_string(),
2,
)
.unwrap_err(),
CalendarError::UnknownTimezone("hello".to_string())
);
}
}