rrules 0.2.3

A library for working with recurrence rules
Documentation
#[cfg(test)]
mod tests {
    use crate::frequencies::Frequency;
    use crate::recurrences::{Recurrence, RecurrenceInvalid};
    use chrono::{DateTime, Duration, Utc};
    use std::str::FromStr;

    fn every_second_recurrence(
        start: DateTime<Utc>,
        end: Option<DateTime<Utc>>,
        duration: Option<Duration>,
    ) -> Result<Recurrence, RecurrenceInvalid> {
        let duration = duration.unwrap_or_else(|| Duration::seconds(1));
        Recurrence::new(
            Frequency::Secondly { interval: 1 },
            start,
            end,
            Some(duration),
        )
    }

    #[test]
    fn test_new_secondly_recurrence() {
        let recurrence = every_second_recurrence(
            DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap(),
            Some(DateTime::<Utc>::from_str("2023-01-01T00:00:02Z").unwrap()),
            None,
        );
        assert!(recurrence.is_ok());
    }

    #[test]
    fn invalid_period() {
        let recurrence = every_second_recurrence(
            DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap(),
            Some(DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap()),
            None,
        );
        assert!(recurrence.is_err());
    }

    #[test]
    fn invalid_duration() {
        let recurrence = every_second_recurrence(
            DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap(),
            Some(DateTime::<Utc>::from_str("2023-01-01T00:00:10Z").unwrap()),
            Some(Duration::hours(1)),
        );
        assert!(recurrence.is_err());
    }
}

#[cfg(test)]
mod secondly_recurrences {
    use crate::frequencies::Frequency;
    use crate::recurrences::Recurrence;
    use chrono::{DateTime, Duration, Utc};
    use std::str::FromStr;

    #[test]
    fn every_second() {
        let start = DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap();
        let end = DateTime::<Utc>::from_str("2023-01-01T00:00:02Z").unwrap();
        let frequency = Frequency::Secondly { interval: 1 };
        let recurrence =
            Recurrence::new(frequency, start, Some(end), Some(Duration::seconds(1))).unwrap();
        let dates: Vec<DateTime<Utc>> = recurrence.collect();
        assert_eq!(dates.len(), 3);
        assert_eq!(
            dates,
            vec![
                DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-01T00:00:01Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-01T00:00:02Z").unwrap(),
            ]
        );
    }
}

#[cfg(test)]
mod minutely_recurrences {
    use crate::frequencies::Frequency;
    use crate::recurrences::Recurrence;
    use chrono::{DateTime, Duration, Utc};
    use std::str::FromStr;

    #[test]
    fn every_minute() {
        let start = DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap();
        let end = DateTime::<Utc>::from_str("2023-01-01T00:02:00Z").unwrap();
        let frequency = Frequency::Minutely { interval: 1 };
        let recurrence =
            Recurrence::new(frequency, start, Some(end), Some(Duration::minutes(1))).unwrap();
        let dates: Vec<DateTime<Utc>> = recurrence.collect();
        assert_eq!(dates.len(), 3);
        assert_eq!(
            dates,
            vec![
                DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-01T00:01:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-01T00:02:00Z").unwrap(),
            ]
        );
    }
}

#[cfg(test)]
mod hourly_recurrences {
    use crate::frequencies::Frequency;
    use crate::recurrences::Recurrence;
    use chrono::{DateTime, Duration, Utc};
    use std::str::FromStr;

    #[test]
    fn every_hour() {
        let start = DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap();
        let end = DateTime::<Utc>::from_str("2023-01-01T02:00:00Z").unwrap();
        let frequency = Frequency::Hourly { interval: 1 };
        let recurrence =
            Recurrence::new(frequency, start, Some(end), Some(Duration::hours(1))).unwrap();
        let dates: Vec<DateTime<Utc>> = recurrence.collect();
        assert_eq!(dates.len(), 3);
        assert_eq!(
            dates,
            vec![
                DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-01T01:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-01T02:00:00Z").unwrap(),
            ]
        );
    }
}

#[cfg(test)]
mod daily_recurrences {
    use crate::frequencies::{Frequency, Time};
    use crate::recurrences::Recurrence;
    use chrono::{DateTime, Duration, Utc};
    use std::str::FromStr;

    #[test]
    fn every_day() {
        let start = DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap();
        let end = DateTime::<Utc>::from_str("2023-01-03T00:00:00Z").unwrap();
        let frequency = Frequency::Daily {
            interval: 1,
            by_time: vec![],
        };
        let recurrence =
            Recurrence::new(frequency, start, Some(end), Some(Duration::days(1))).unwrap();
        let dates: Vec<DateTime<Utc>> = recurrence.collect();
        assert_eq!(dates.len(), 3);
        assert_eq!(
            dates,
            vec![
                DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-02T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-03T00:00:00Z").unwrap(),
            ]
        );
    }

    #[test]
    fn every_day_twice_a_day() {
        let start = DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap();
        let end = DateTime::<Utc>::from_str("2023-01-03T00:00:00Z").unwrap();
        let frequency = Frequency::Daily {
            interval: 1,
            by_time: vec![
                Time::from_str("09:00:00").unwrap(),
                Time::from_str("18:00:00").unwrap(),
            ],
        };
        let recurrence =
            Recurrence::new(frequency, start, Some(end), Some(Duration::hours(1))).unwrap();
        let dates: Vec<DateTime<Utc>> = recurrence.collect();
        assert_eq!(dates.len(), 4);
        assert_eq!(
            dates,
            vec![
                DateTime::<Utc>::from_str("2023-01-01T09:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-01T18:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-02T09:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-02T18:00:00Z").unwrap(),
            ]
        );
    }
}

#[cfg(test)]
mod weekly_recurrences {
    use crate::frequencies::Frequency;
    use crate::recurrences::Recurrence;
    use chrono::{DateTime, Duration, Utc, Weekday};
    use std::str::FromStr;

    #[test]
    fn weekly_recurrence() {
        let start = DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap();
        let end = DateTime::<Utc>::from_str("2023-01-15T00:00:00Z").unwrap();
        let frequency = Frequency::Weekly {
            interval: 1,
            by_day: vec![],
        };
        let recurrence =
            Recurrence::new(frequency, start, Some(end), Some(Duration::weeks(1))).unwrap();
        let dates: Vec<DateTime<Utc>> = recurrence.collect();
        assert_eq!(dates.len(), 3);
        assert_eq!(
            dates,
            vec![
                DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-08T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-15T00:00:00Z").unwrap(),
            ]
        );
    }

    #[test]
    fn weekly_by_day_recurrence() {
        let start = DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap();
        let end = DateTime::<Utc>::from_str("2023-01-15T00:00:00Z").unwrap();
        let frequency = Frequency::Weekly {
            interval: 1,
            by_day: vec![Weekday::Mon, Weekday::Wed, Weekday::Fri],
        };
        let recurrence =
            Recurrence::new(frequency, start, Some(end), Some(Duration::hours(1))).unwrap();
        let dates: Vec<DateTime<Utc>> = recurrence.collect();
        assert_eq!(dates.len(), 6);
        assert_eq!(
            dates,
            vec![
                DateTime::<Utc>::from_str("2023-01-02T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-04T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-6T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-09T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-11T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-13T00:00:00Z").unwrap(),
            ]
        );
    }
}

#[cfg(test)]
mod monthly_recurrences {
    use crate::frequencies::{Frequency, NthWeekday};
    use crate::recurrences::Recurrence;
    use chrono::{DateTime, Duration, Utc, Weekday};
    use std::str::FromStr;

    #[test]
    fn monthly_recurrence() {
        let start = DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap();
        let end = DateTime::<Utc>::from_str("2023-03-01T00:00:00Z").unwrap();
        let frequency = Frequency::Monthly {
            interval: 1,
            by_month_day: vec![],
            nth_weekdays: vec![],
        };
        let recurrence =
            Recurrence::new(frequency, start, Some(end), Some(Duration::weeks(1))).unwrap();
        let dates: Vec<DateTime<Utc>> = recurrence.collect();
        assert_eq!(dates.len(), 3);
        assert_eq!(
            dates,
            vec![
                DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-02-01T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-03-01T00:00:00Z").unwrap(),
            ]
        );
    }

    #[test]
    fn monthly_recurrence_by_month_day() {
        let start = DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap();
        let end = DateTime::<Utc>::from_str("2023-02-20T00:00:00Z").unwrap();
        let frequency = Frequency::Monthly {
            interval: 1,
            by_month_day: vec![1, 15],
            nth_weekdays: vec![],
        };
        let recurrence =
            Recurrence::new(frequency, start, Some(end), Some(Duration::weeks(1))).unwrap();
        let dates: Vec<DateTime<Utc>> = recurrence.collect();
        assert_eq!(dates.len(), 4);
        assert_eq!(
            dates,
            vec![
                DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-15T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-02-01T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-02-15T00:00:00Z").unwrap(),
            ]
        );
    }

    #[test]
    fn monthly_recurrence_by_week_number() {
        let start = DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap();
        let end = DateTime::<Utc>::from_str("2023-02-20T00:00:00Z").unwrap();
        let frequency = Frequency::Monthly {
            interval: 1,
            by_month_day: vec![],
            nth_weekdays: vec![
                NthWeekday::new(Weekday::Wed, 1),
                NthWeekday::new(Weekday::Fri, 1),
            ],
        };
        let recurrence =
            Recurrence::new(frequency, start, Some(end), Some(Duration::hours(1))).unwrap();
        let dates: Vec<DateTime<Utc>> = recurrence.collect();
        assert_eq!(dates.len(), 4);
        assert_eq!(
            dates,
            vec![
                DateTime::<Utc>::from_str("2023-01-04T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-01-06T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-02-01T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2023-02-03T00:00:00Z").unwrap(),
            ]
        );
    }
}

#[cfg(test)]
mod yearly_recurrences {
    use crate::frequencies::{Frequency, MonthlyDate};
    use crate::recurrences::Recurrence;
    use chrono::{DateTime, Duration, Month, Utc};
    use std::str::FromStr;

    #[test]
    fn test_once_a_year() {
        let start = DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap();
        let end = DateTime::<Utc>::from_str("2025-01-01T00:00:00Z").unwrap();
        let frequency = Frequency::Yearly {
            interval: 1,
            by_monthly_date: None,
        };
        let recurrence =
            Recurrence::new(frequency, start, Some(end), Some(Duration::weeks(1))).unwrap();
        let dates: Vec<DateTime<Utc>> = recurrence.collect();
        assert_eq!(dates.len(), 3);
        assert_eq!(
            dates,
            vec![
                DateTime::<Utc>::from_str("2023-01-01T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2024-01-01T00:00:00Z").unwrap(),
                DateTime::<Utc>::from_str("2025-01-01T00:00:00Z").unwrap(),
            ]
        );
    }
}