Dealing with Recurrence Rule can be pain. I have seen people facing problem in understanding & parsing Rrule though we have a detailed RFC around it. I guess difficulty comes from the fact that there are so many different recurrence patterns to deal with.
Here we would be exploring a few libraries to find out last occurrence date of a recurring iCalendar event. Basically we are trying to figure out the validity of the event & identify the last recurrence date.
If we try to write it from scratch, that would be a very time consuming task. We don’t need to reinvent the wheel. We will use one Python library & one NodeJS library to simplify things. NodeJS library is based on the Python library. So logic & implementation is similar. It just depends on which language you choose.
These libraries support finding the last occurrence before a given date. They also support finding first occurrence after a given date. So logic would be simple.
- Choose a threshold date in future based on your business use case, let’s say 01-01-2100.
- Use the library to find the first occurrence after 01-01-2100.
- If you get valid entry, then most likely this Recurrence Rule is never expiring. You can assume last date as 01-01-2100.
- If you get null, that means the event recurrence pattern expires before 01-01-2100.
- Use the same library to find the last occurrence before 01-01-2100. That would be your last recurrence date.
Here is sample code in Python based on python-dateutil library:
>>> from dateutil.rrule import *
>>> from dateutil.parser import *
>>> from datetime import *
>>> import pytz
>>> threshold = datetime(2100, 01, 01, 00, 00, 00, 0, pytz.UTC)
>>> test_rule=rrulestr('DTSTART:20220201T022000Z\nRRULE:FREQ=MONTHLY;COUNT=7');
>>> print(test_rule.after(threshold))
None
>>> print(test_rule.before(threshold))
2022-08-01 02:20:00+00:00
>>>
Similar code implementation for NodeJS is as follows:
> let { RRule } = require("rrule");
undefined
> let threshold = new Date(Date.UTC(2100, 01, 01, 00, 00, 0));
undefined
> let rrule = RRule.fromString("DTSTART:20220201T022000Z\nRRULE:FREQ=MONTHLY;COUNT=7");
undefined
> console.log(rrule.after(threshold));
null
undefined
> console.log(rrule.before(threshold));
2022-08-01T02:20:00.000Z
I haven’t tested the libraries thoroughly. It worked well in most of the cases that I tested. I got pretty accurate results. Feel free to test them out & validate your test cases. Then you can handle it accordingly.
Anyways it is better than writing something from scratch. And you can even deploy them as AWS Lambda or Google Cloud Functions because both Python & NodeJS are supported.