rrule 0.8.0

A pure Rust implementation of recurrence rules as defined in the iCalendar RFC.
Documentation

A performant rust implementation of recurrence rules as defined in the iCalendar RFC.

This crate provides [RRuleSet] for working with recurrence rules. It has a collection of DTSTART, RRULEs, EXRULEs, RDATEs and EXDATEs. Both the RRULE and EXRULE properties are represented by the [RRule] type and the DTSTART, RDATE and EXDATE properties are represented by the [chrono::DateTime<Tz>].

Building RRule and RRuleSet

Both types implements the [std::str::FromStr] trait so that they can be parsed and built from a string representation. [RRuleSet] can also be built by composing multiple RRules for its rrule and exrule properties and [chrono::DateTime<Tz>] for its dt_start, exdate and rdate properties. See the examples below.

Generating occurrences

You can loop over the occurrences of a [RRuleSet] by calling any of the following methods:

  • [RRuleSet::all]: Generate all recurrences that match the rules (with a limit to prevent infinite loops).
  • [RRuleSet::all_between]: Generate all recurrences that match the rules and are between two given dates.
  • [RRuleSet::just_before]: Generate the last recurrence that matches the rules and is before a given date.
  • [RRuleSet::just_after]: Generate the first recurrence that matches the rules and is after a given date.
  • ...

If you have some additional filters or want to work with infinite recurrence rules [RRuleSet] implements the Iterator trait which makes them very flexible. All the methods above uses the iterator trait in its implementation as shown below.

use chrono::{DateTime, TimeZone};
use chrono_tz::UTC;
use rrule::{RRuleSet};

let rrule: RRuleSet = "DTSTART:20120201T093000Z\nRRULE:FREQ=DAILY;COUNT=3".parse().unwrap();

// All dates
assert_eq!(
vec![
DateTime::parse_from_rfc3339("2012-02-01T09:30:00+00:00").unwrap(),
DateTime::parse_from_rfc3339("2012-02-02T09:30:00+00:00").unwrap(),
DateTime::parse_from_rfc3339("2012-02-03T09:30:00+00:00").unwrap(),
],
rrule.all(100).unwrap()
);

Find all events that are within a given range.

# use chrono::{DateTime, TimeZone};
# use chrono_tz::UTC;
# use rrule::{RRuleSet};
#
let rrule: RRuleSet = "DTSTART:20120201T093000Z\nRRULE:FREQ=DAILY;COUNT=3".parse().unwrap();
// Between two dates
let after = UTC.ymd(2012, 2, 1).and_hms(10, 0, 0);
let before = UTC.ymd(2012, 4, 1).and_hms(9, 0, 0);
let inc = true; // Whether dates equal to after or before should be added;

assert_eq!(
vec![
DateTime::parse_from_rfc3339("2012-02-02T09:30:00+00:00").unwrap(),
DateTime::parse_from_rfc3339("2012-02-03T09:30:00+00:00").unwrap(),
],
rrule.all_between(after, before, inc).unwrap()
);

Note: All the generated recurrence will be in the same time zone as the dt_start property.

Example

Quick start by parsing strings

use chrono::DateTime;
use rrule::{RRuleSet};

// Parse a RRule string
let rrule: RRuleSet = "DTSTART:20120201T093000Z\n\
RRULE:FREQ=WEEKLY;INTERVAL=5;UNTIL=20130130T230000Z;BYDAY=MO,FR".parse().unwrap();
assert_eq!(rrule.all(100).unwrap().len(), 21);

// Parse a RRuleSet string
let rrule_set: RRuleSet = "DTSTART:20120201T023000Z\n\
RRULE:FREQ=MONTHLY;COUNT=5\n\
RDATE:20120701T023000Z,20120702T023000Z\n\
EXRULE:FREQ=MONTHLY;COUNT=2\n\
EXDATE:20120601T023000Z".parse().unwrap();
let all_dates = rrule_set.all(100).unwrap();
assert_eq!(all_dates.len(), 4);

assert_eq!(
vec![
DateTime::parse_from_rfc3339("2012-04-01T02:30:00+00:00").unwrap(),
DateTime::parse_from_rfc3339("2012-05-01T02:30:00+00:00").unwrap(),
DateTime::parse_from_rfc3339("2012-07-01T02:30:00+00:00").unwrap(),
DateTime::parse_from_rfc3339("2012-07-02T02:30:00+00:00").unwrap(),
],
all_dates
);