From 4116776e1d78568febf98b183738ae82263f01eb Mon Sep 17 00:00:00 2001 From: Ruben Pollan Date: Wed, 29 Sep 2021 20:38:30 +0200 Subject: Add support to BYDAY in MONTHLY frequency --- src/date.rs | 20 ++++++++++++++++++++ src/periodic.rs | 45 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/src/date.rs b/src/date.rs index 71e29c7..c3cf816 100644 --- a/src/date.rs +++ b/src/date.rs @@ -79,6 +79,13 @@ impl Date { } } + pub fn with_day(&self, day: u32) -> Option { + Some(match *self { + Date::Time(t) => Date::Time(t.with_day(day)?), + Date::AllDay(d) => Date::AllDay(d.with_day(day)?), + }) + } + pub fn weekday(&self) -> Weekday { match *self { Date::Time(t) => t.weekday(), @@ -100,6 +107,12 @@ impl Date { }) } + pub fn week_of_month(&self) -> (i32, i32) { + let week = ((self.day() - 1) / 7 + 1) as i32; + let neg_week = ((self.days_in_month() - self.day()) / 7 + 1) as i32; + (week, -neg_week) + } + pub fn year(&self) -> i32 { match *self { Date::Time(t) => t.year(), @@ -113,6 +126,13 @@ impl Date { Date::AllDay(d) => Date::AllDay(d.with_year(year)?), }) } + + pub fn days_in_month(&self) -> u32 { + chrono::NaiveDate::from_ymd_opt(self.year(), self.month() + 1, 1) + .unwrap_or(chrono::NaiveDate::from_ymd(self.year() + 1, 1, 1)) + .pred() + .day() + } } impl Ord for Date { diff --git a/src/periodic.rs b/src/periodic.rs index 19874e4..c0d0ad3 100644 --- a/src/periodic.rs +++ b/src/periodic.rs @@ -1,5 +1,6 @@ use std::fmt; use std::str::FromStr; +use std::ops::Add; use std::collections::HashMap; use chrono::{Duration, Weekday}; @@ -129,16 +130,40 @@ impl<'a> Iter<'a> { } } Freq::Monthly => { - // TODO: byday... - let new_date = if date.month() == 12 { - date.with_month(1) - .unwrap() - .with_year(date.year() + 1) - .unwrap() - } else { - date.with_month(date.month() + 1).unwrap() - }; - new_date - date + match &p.byday { + Some(byday) => { + let mut next = date; + if p.interval > 1 { + for _ in 1..p.interval { + next = next.add(Duration::days(next.days_in_month().into())); + } + } + loop { + next = next.add(Duration::days(1)); + let (week, neg_week) = next.week_of_month(); + + match byday.get(&next.weekday()) { + Some(occurrences) => + if occurrences.contains(&0) || occurrences.contains(&week) ||occurrences.contains(&neg_week) { + break; + } + None => {} + }; + }; + next - date + } + None => { + let new_date = if date.month() == 12 { + date.with_month(1) + .unwrap() + .with_year(date.year() + 1) + .unwrap() + } else { + date.with_month(date.month() + 1).unwrap() + }; + new_date - date + } + } } // TODO: byday... Freq::Yearly => date.with_year(date.year() + 1).unwrap() - date, -- cgit v1.2.3