Add Recurring Rules for Calendar Dates to your Application
Spare yourself the pain and suffering by following my journey (and solution) for adding recurring events to your application.
My project has a requirement to allow users to set reminders. Reminders can be a one-time or recurring event for any given subject. One-time events are simple enough but once I started thinking about recurring events I realized it can get complicated real fast. 😕
“I need to set a reminder starting next Tuesday for every other Monday, Tuesday and Friday on odd months for 10 occurrences…”
Research Stage
Not knowing exactly what to search for, I set about my research quest. Obviously calendar applications have dealt with recurring events for years so I started there.
This lead me to RFC 5545 Internet Calendaring and Scheduling Core Object Specification. If you are having trouble sleeping I highly recommend pulling up this specification and giving it a read! 😴
I came to the section entitled “Recurrence Rule” (page 124 for those following along). It was here that I discovered the [bizarre] syntax known as RRULE.
Now I knew what I was searching for so I headed over to NPM and did a search for “rrule”. Low and behold I found the rrule.js package.
rrule.js supports recurrence rules as defined in the iCalendar RFC, with a few important differences. It is a partial port of the
rrule
module from the excellent python-dateutil library. On top of that, it supports parsing and serialization of recurrence rules from and to natural language.
Prototyping Stage
For my prototype I built a new project using Quasar Framework (which also provides a full featured Calendar app extension). Quasar Framework runs on top of Vue.js.
NOTE: Everything I’m building here will work with any JavaScript framework and Calendar Component. You will just need to massage the data to fit your components requirements.
For my test data I settled on creating reminders to take out the trash. I knew my wife would approve! 😄 My trash pickup schedule requires that I put out the trash on the following schedule:
- Take out trash bin every week on Sunday and Wednesday at 9 pm
- Take out recycle bin every week on Wednesday at 9 pm
- Take out bulk trash every month on the 4th Wednesday at 9 pm
The next step was to translate these reminders into RRule syntax. An RRule object can be created by either parsing RRule JSON or RRule string formats. The creators of this library created a demo page that acts as a sandbox for playing with different options and seeing their results.
These are the RRule JSON and String representations of my first chore.
- Frequency is weekly
- Starting tonight @ 9pm
- Time zone set to my local time zone because all values are stored as UTC
- Interval is every week
- Occurring on Wednesdays and Sundays
My second chore schedule was very similar. The only change necessary was to remove the Sunday occurrence.
My last chore had a couple of rule differences.
- Frequency was monthly
- Every 4th Wednesday was defined with the
bysetpos
value
I verified these rules (and numerous alternatives) on the demo page so at this point I’m comfortable moving on to coding.
Coding Stage
At a minimum I needed to facilitate managing reminders and then provide a way to display the recurrence of those reminders in human-readable form.
Managing Reminders
Managing reminders is just a matter of providing CRUD operations but with the unique addition of handling RRULEs. Creating an RRULE form is not something I’m going to cover here as that topic could be an article on its own.
So, now the question was how to I store the RRULE for each reminder? I need to save the RRULE in a format that can be saved externally and parsed after retrieval. My two options are the RRULE JSON and String formats. Since the RRULE JSON format requires RRULE expressions I decided to save RRULE in their RRULE string format.

I created a relatively simple page that contains a table component that lists all of my reminders and a form component for adding and editing reminders. I also enable a Delete Button when one or more reminders are selected.
In the form there are two editable fields: Description and RRule String.
As you enter a valid RRULE string the RRule Text and Sample Schedule fields will immediately display their values from the rrule.js methods. This is a helpful validation for your String.
NOTE: To enter the RRule string I used the rrule.js demo page to define my rule and then copied and pasted the resulting string into my form.
In my Vue.js component I added a watch
to trigger for any time the RRule String value is changed. The framework agnostic portion of the code is as follows:
The value from the RRule String field is stored in the rruleString
variable. I then use the rrulestr()
method to parse the RRule String. If that results in a value RRule object I can use the toText()
and all()
methods to extract and populate the RRule Text and Sample Schedule fields. These methods are all provided by the rrule.js package and their documentation provided the example for getting the first five occurrences.
Once Saved, each reminder is stored as a JavaScript object:
Displaying Reminders
Now that my reminders are being created, updated and deleted I moved on to the display portion of the code. 😵
First I like to just display my data in the raw to make sure I’m getting the data I’m expecting.

Next I wanted to include the parsed RRULE and to display the next few upcoming reminders for each chore.

This display does show the actual schedule data but the is not ideal for the situation. The display should be grouped by the date and then time and then by chore.

That looks better. 🔮🦄 (← that means it was “magic”)
Notice I changed the time for the “Take out bulk trash” just to make sure the times were grouping properly.
OK, that’s it! :
Well… almost. I would feel like I’ve cheated you if I did not include this last part.
Working with Dates
It took me about 24 hours to build this scenario. It should have taken half that but I ran into problems with date and time inconsistencies.😬
Working with dates and times can always be problematic, especially when you can’t guarantee that your application and the consumer are in the same time zone. Getting time zone settings wrong can result in a mess. The rrule.js library states numerous times that to use UTC (otherwise known as GMT or Zulu) as it is the basis for local times worldwide.
First of all, I had to go back and add TZID
and my time zone in my RRule string. This is required if I’m entering the local date and time which is normally what happens with end user input.
After this change I was still unable to get the between()
method to return the correct results. The results were always outside of my date range. Then I noticed the rrule.js documentation showed the same problem!


There was another example in their documentation but why beat a dead horse, am I right? 🐴 ❓
Notice how the returned dates are outside the between()
scope? At least I felt better seeing this knowing it wasn’t just me! 😆
I spent a little bit of time looking at alternative libraries but soon gave up on that idea. Then I started playing with the moment.js library to help calculate future dates. I use this library for date and time related features in all of my projects.
After hours [of pulling out my hair] I finally dumped the use of JavaScript Date (e.g. new Date()
)completely and instead relied 100% on moment.js methods. I can’t explain why this worked but it did. For the first time I was getting my expected results from rrule.js. To give you an idea of what I mean, take a look at the function I wrote to handle the grouping of my reminders.
NOTE: I am also using the lodash library to simplify the sorting, iterating and grouping.
The reminders
variable that is passed into this function contains an array of objects. Each object is a chore and looks like this (the rrule element has already been parsed into an RRule object):
{
id: 1,
descr: 'Take out the trash',
rrule: {...}
}
Now we need to break out some of the upcoming dates from our RRule object, group them by date, group them again by time and then list out the chores that occur within that date and time grouping.
The groupByDate
function (above) does just that and the results look something like this:
Conclusion
I hope this article helps you fast track adding recurring events to your application. I feel like everyone can relate to scheduled events since we use them all of the time, even daily. Now you can add them to your own application or, perhaps, this will inspire you to write your own “Reminder” app. 👍