use chrono::{Months, NaiveDateTime};
use crate::error::{FlexitimeError, FlexitimeResult};
#[derive(Debug, PartialEq, Default)]
pub struct RelativeTime {
pub seconds: Option<u32>,
pub minutes: Option<u32>,
pub hours: Option<u32>,
pub days: Option<u32>,
pub weeks: Option<u32>,
pub months: Option<u32>,
pub years: Option<u32>,
pub negative: bool,
}
impl RelativeTime {
pub fn new() -> Self {
RelativeTime::default()
}
pub fn ago(mut self) -> Self {
self.negative = true;
self
}
pub fn hence(mut self) -> Self {
self.negative = false;
self
}
pub fn seconds(mut self, seconds: u32) -> Self {
self.seconds = Some(seconds);
self
}
pub fn minutes(mut self, minutes: u32) -> Self {
self.minutes = Some(minutes);
self
}
pub fn hours(mut self, hours: u32) -> Self {
self.hours = Some(hours);
self
}
pub fn days(mut self, days: u32) -> Self {
self.days = Some(days);
self
}
pub fn weeks(mut self, weeks: u32) -> Self {
self.weeks = Some(weeks);
self
}
pub fn months(mut self, months: u32) -> Self {
self.months = Some(months);
self
}
pub fn years(mut self, years: u32) -> Self {
self.years = Some(years);
self
}
pub fn to_chrono(&self, mut base_time: NaiveDateTime) -> FlexitimeResult<NaiveDateTime> {
let sign = if self.negative { -1 } else { 1 };
if let Some(seconds) = self.seconds {
base_time += chrono::Duration::seconds((seconds as i64) * sign);
}
if let Some(minutes) = self.minutes {
base_time += chrono::Duration::minutes((minutes as i64) * sign);
}
if let Some(hours) = self.hours {
base_time += chrono::Duration::hours((hours as i64) * sign);
}
if let Some(days) = self.days {
base_time += chrono::Duration::days((days as i64) * sign);
}
if let Some(weeks) = self.weeks {
base_time += chrono::Duration::weeks((weeks as i64) * sign);
}
if let Some(months) = self.months {
let months = Months::new(months);
base_time = if self.negative {
base_time.checked_sub_months(months)
} else {
base_time.checked_add_months(months)
}
.ok_or(FlexitimeError::InvalidMonths)?;
}
if let Some(years) = self.years {
let months = Months::new(years * 12);
base_time = if self.negative {
base_time.checked_sub_months(months)
} else {
base_time.checked_add_months(months)
}
.ok_or(FlexitimeError::InvalidYears)?;
}
Ok(base_time)
}
}
#[cfg(test)]
mod tests {
use chrono::{Duration, Utc};
use super::*;
#[test]
fn test_to_datetime() {
let base_time = Utc::now().naive_utc();
let time = RelativeTime::new()
.years(1)
.months(2)
.weeks(3)
.days(4)
.hours(5)
.minutes(6)
.seconds(7)
.to_chrono(base_time.clone())
.unwrap();
let mut new_time = base_time
+ Duration::seconds(7)
+ Duration::minutes(6)
+ Duration::hours(5)
+ Duration::days(4)
+ Duration::weeks(3);
new_time = new_time.checked_add_months(Months::new(2)).unwrap();
new_time = new_time.checked_add_months(Months::new(1 * 12)).unwrap();
assert_eq!(time, new_time)
}
#[test]
fn test_to_datetime_negative() {
let base_time = Utc::now().naive_utc();
let time = RelativeTime::new()
.years(1)
.months(2)
.weeks(3)
.days(4)
.hours(5)
.minutes(6)
.seconds(7)
.ago()
.to_chrono(base_time.clone())
.unwrap();
let mut new_time = base_time
- Duration::seconds(7)
- Duration::minutes(6)
- Duration::hours(5)
- Duration::days(4)
- Duration::weeks(3);
new_time = new_time.checked_sub_months(Months::new(2)).unwrap();
new_time = new_time.checked_sub_months(Months::new(1 * 12)).unwrap();
assert_eq!(time, new_time)
}
}