rSchedule: a javascript recurring dates library
Tags: typescript, javascript, githunt, library
Published: 2018-09-03 15:29:31

---
title: rSchedule: a javascript recurring dates library
published: true
description: An awesome javascript library for working with recurring dates. Rules can be imported / exported in ICAL format.
tags: typescript, javascript, githunt, library
---

[rSchedule](https://gitlab.com/john.carroll.p/rschedule) is a javascript library, written in typescript, for working with recurring dates and ICal recurrences. Rules can be imported / exported in [iCalendar RFC 5545](https://tools.ietf.org/html/rfc5545) format, and Rule objects themselves adhere to the [javascript iterator protocol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators).

Example usage:

```typescript
const rule = new Rule({
  frequency: 'YEARLY',
  byMonthOfYear: [2, 6],
  byDayOfWeek: ['SU', ['MO', 3]],
  start: new Date(2010,1,7),
})

let index = 0;
for (const { date } of rule.occurrences()) {
  date.toISOString()
  index++
  
  if (index > 10) break;
}

rule.occurrences({
  start: new Date(2010,5,7),
  take: 5
})
  .toArray()
  .map(date => date.toISOString())
```

rSchedule makes use of a fairly simple `DateAdapter` wrapper object which abstracts away from individual date library implementations, making rSchedule date library agnostic. If your chosen `DateAdapter` supports time zones, rSchedule supports time zones.

`StandardDateAdapter`, `LuxonDateAdapter`, `MomentDateAdapter`, and `MomentTZDateAdapter` packages currently exists which provide a `DateAdapter` complient wrapper for the standard javascript `Date` object, as well as [`moment`](https://momentjs.com), [`moment-timezone`](https://momentjs.com), and [luxon `DateTime`](https://moment.github.io/luxon/) objects. Additionally, it should be pretty easy for you to create your own `DateAdapter` for your preferred library.

rSchedule has been coded from scratch to facilitate the creation of complex recurring schedules and there's a lot packed in. [For a complete overview, check out the project on Gitlab](https://gitlab.com/john.carroll.p/rschedule).

### Occurrence Stream Operators

Without delving too deeply into the library, I want to call out one, very cool feature which it has: occurrence stream operators.

Occurrence stream operators are inspired by rxjs pipe operators, and they allow you to combine and manipulate occurrence streams from different objects.

An example from my own app:

> I want to get the volunteer schedule for a particular volunteer. A volunteer schedule is the *intersection* of the schedule a person signs up for and the occurrence schedule of the associated volunteer opportunity.

> Put another way, a volunteer opportunity has its own occurrence schedule. Lets say: every **other** Tuesday as well as every Friday. Separately, someone might sign up to volunteer "every tuesday", as well as a few, specific, Fridays. The days that an individual is *actually* going to volunteer though, are the intersection of these two schedules.

Here's how you could build this new schedule using rSchedule:

```typescript
declare const volunteerAvailability: Schedule[];
declare const opportunitySchedule: Calendar;

const volunteerSchedule = new Calendar().pipe(
    add(...volunteerAvailability),
    unique(),
    intersection({
      streams: opportunitySchedule
    })
  );
```

Breaking this example down:

- `volunteerAvailability` contains an array different schedules when a volunteer has indicated that they are available.
- `opportunitySchedule` contains a calendar describing when the volunteer opportunity actually occurs.

We want to create a calendar containing the times when the volunteer is available to participate *in this volunteer opportunity* (the `volunteerSchedule`).

```typescript
const volunteerSchedule =

  // create a new calendar
  new Calendar().pipe(

    // add all times that the volunteer is available, in general
    add(...volunteerAvailability),

    // filter to get only unique times
    unique(),

    // get the intersection of these times with the
    // volunteer opportunity's schedule
    intersection({
      streams: opportunitySchedule
    })
  );
```

The resulting `volunteerSchedule` can be iterated over using `volunteerSchedule.occurrences()`. I can also easily group occurrences by month using `volunteerSchedule.collections({granularity: 'MONTHLY'})` and iterate over the months.

Considering each volunteer that signs up will have their own schedule, I can then combine each of *these schedules* together to create a new calendar containing the dates which every person is scheduled to volunteer.

For example:

```typescript
declare const volunteerSchedules: Calendar[];

const scheduleOfAllVolunteers =
  new Calendar({ schedules: volunteerSchedules });
```

I imagine most users will not need such powerful tools, and for them they can just use the provided `Schedule` object which implements `RRULE`, `EXRULE`, `RDATE`, and `EXDATE` from the ICAL spec. 

For example:

```typescript
const schedule = new Schedule({
  rrules: [
    {
      frequency: 'WEEKLY',
      start: new Date(2012, 5, 24),
      end: new Date(2012, 11, 31)
    },
    {
      frequency: 'DAILY',
      start: new Date(2011, 9, 2)
    }
  ],
  data: 'Holds anything I want',
})

schedule
  .occurrences({take: 10})
  .toArray()
  .map(date => date.toISOString())
```

# [To learn more, check out the rSchedule repo.](https://gitlab.com/john.carroll.p/rschedule)