Why it matters
Recurrence rules look simple until they meet real calendars: the last Friday of each month, the 31st in months that have only 30 days, every other week with the week starting on Sunday, or a meeting at 1:30am on the day the clocks change. Calendar apps disagree about edge cases, and most online expanders ignore time zones.
This expander implements RFC 5545's rules, including ordinal weekdays, BYSETPOS and week numbers, and its results match python-dateutil, the reference most server code uses, on the test rules. Occurrences are built in the event's own time zone, then converted to UTC using your browser's time zone data, so you can see the ones that land in a daylight saving gap or overlap.
How to use it
- Paste DTSTART and RRULE lines from an .ics file, or just an RRULE.
- Choose the time zone if there's no TZID.
- Read the occurrences and any DST notes.
- Copy the CSV if you need the dates elsewhere.
FAQ
What does BYSETPOS do?
It picks positions from the set of matches in each period. FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1 is the last weekday of each month.
Does COUNT include dates removed by EXDATE?
Yes. COUNT limits the instances the rule generates; EXDATE then removes some of them.
What happens to an event at 1:30am when the clocks go forward?
RFC 5545 says to use the offset from before the gap, so it moves to 2:30am. If the clocks go back, the first 1:30am is used.
What if the 31st doesn't exist in a month?
That month is skipped. RFC 5545 ignores invalid dates rather than moving them.