diff --git a/src/calendar.rs b/src/calendar.rs index dcc2040..60788a6 100644 --- a/src/calendar.rs +++ b/src/calendar.rs @@ -1,3 +1,4 @@ +use crate::date_range::*; use chrono::naive::NaiveDate; use chrono::{Datelike, Month, Weekday}; use serde::{Deserialize, Serialize}; @@ -65,11 +66,14 @@ pub enum DateSpec { impl DateSpec { /// Get exact date of event and its policy, if it occured in that year - fn resolve(&self, year: i32) -> Option<(NaiveDate, AdjustmentPolicy)> { + fn resolve(&self, year: i32) -> Option<(DateRange, AdjustmentPolicy)> { use DateSpec::*; match self { - SpecificDate { date, .. } => Some((*date, AdjustmentPolicy::NoAdjustment)), + SpecificDate { date, .. } => Some(( + DateRange::new(*date, date.succ()), + AdjustmentPolicy::NoAdjustment, + )), DayOfMonth { month, day, @@ -83,10 +87,8 @@ impl DateSpec { } else if until.is_some() && until.unwrap().year() < year { None } else { - Some(( - NaiveDate::from_ymd(year, month.number_from_month(), *day), - *observed, - )) + let date = NaiveDate::from_ymd(year, month.number_from_month(), *day); + Some((DateRange::new(date, date.succ()), *observed)) } } NthDayOccurance { @@ -118,7 +120,7 @@ impl DateSpec { date = date.checked_sub_signed(chrono::Duration::days(7)).unwrap(); off += 1; } - Some((date, *observed)) + Some((DateRange::new(date, date.succ()), *observed)) } else { let mut date = NaiveDate::from_ymd(year, month_num, 1); while date.weekday() != *dow { @@ -129,7 +131,7 @@ impl DateSpec { date = date.checked_add_signed(chrono::Duration::days(7)).unwrap(); off -= 1; } - Some((date, *observed)) + Some((DateRange::new(date, date.succ()), *observed)) } } } @@ -157,15 +159,17 @@ pub struct Calendar { } impl Calendar { - fn adjust_workdays(&self, workdays: &Vec<(NaiveDate, AdjustmentPolicy)>) -> HashSet { + fn adjust_workdays(&self, workdays: &Vec<(DateRange, AdjustmentPolicy)>) -> HashSet { let mut observed = HashSet::new(); - for (date, policy) in workdays.iter() { - match self.adjust_workday(*date, policy, &observed) { - Some(workday) => { - observed.insert(workday); + for (date_range, policy) in workdays.iter() { + for date in date_range { + match self.adjust_workday(date, policy, &observed) { + Some(workday) => { + observed.insert(workday); + } + None => {} } - None => {} } } @@ -225,7 +229,7 @@ impl Calendar { /// Get the set of all workdays in a given year pub fn get_workdays(&self, date: NaiveDate) -> HashSet { - let workdays: Vec<(NaiveDate, AdjustmentPolicy)> = self + let workdays: Vec<(DateRange, AdjustmentPolicy)> = self .exclude .iter() .map(|x| x.resolve(date.year())) diff --git a/src/date_range.rs b/src/date_range.rs new file mode 100644 index 0000000..926315d --- /dev/null +++ b/src/date_range.rs @@ -0,0 +1,102 @@ +use chrono::naive::NaiveDate; + +/* +pub type DateRange = Range; +fn iter_dates(range: DateRange) -> impl Iterator { + let ndays = (range.end - range.start).num_days(); + (0..ndays).map(move |x| range.start.checked_add_signed(Duration::days(x)).unwrap()) +} +*/ + +pub struct DateRange { + start: NaiveDate, + end: NaiveDate, +} + +impl DateRange { + pub fn new(start: NaiveDate, end: NaiveDate) -> Self { + DateRange { start, end } + } + + pub fn contains(&self, date: NaiveDate) -> bool { + self.start <= date && date < self.end + } + + pub fn is_empty(&self) -> bool { + self.start == self.end + } +} + +pub struct DateRangeIterator { + cur: NaiveDate, + end: NaiveDate, +} + +impl Iterator for DateRangeIterator { + type Item = NaiveDate; + fn next(&mut self) -> Option { + if self.cur == self.end { + None + } else { + let ret = self.cur; + self.cur = self.cur.succ(); + Some(ret) + } + } +} + +impl IntoIterator for DateRange { + type Item = NaiveDate; + type IntoIter = DateRangeIterator; + + fn into_iter(self) -> Self::IntoIter { + DateRangeIterator { + cur: self.start, + end: self.end, + } + } +} + +impl IntoIterator for &DateRange { + type Item = NaiveDate; + type IntoIter = DateRangeIterator; + + fn into_iter(self) -> Self::IntoIter { + DateRangeIterator { + cur: self.start, + end: self.end, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_range_bounds() { + let dr = DateRange { + start: NaiveDate::from_ymd(2021, 1, 1), + end: NaiveDate::from_ymd(2021, 2, 1), + }; + + assert!(!dr.is_empty()); + assert!(dr.contains(NaiveDate::from_ymd(2021, 1, 1))); + assert!(dr.contains(NaiveDate::from_ymd(2021, 1, 15))); + assert!(!dr.contains(NaiveDate::from_ymd(2021, 2, 1))); + } + + #[test] + fn test_range_iteration() { + let dr = DateRange { + start: NaiveDate::from_ymd(2021, 1, 1), + end: NaiveDate::from_ymd(2021, 2, 1), + }; + + let mut cnt = 0; + for _ in dr { + cnt += 1; + } + assert_eq!(cnt, 31); + } +} diff --git a/src/main.rs b/src/main.rs index b0914e7..c2ec375 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ pub mod calendar; +pub mod date_range; pub mod schedule; fn main() {