An RRULE is the line in an iCalendar (.ics) event that stores a repeating pattern instead of every date, such as RRULE:FREQ=WEEKLY;BYDAY=MO,WE for "every Monday and Wednesday". A calendar app computes the dates from the rule each time it shows the event, so a weekly meeting that ran fifty times is still one event in the file. That is why a spreadsheet, a CRM import, or a simple converter often shows one row where there were fifty meetings.

This post explains how a recurring event is stored, what each part of the rule means, the rules that surprise people, and how to list the real dates. FindUtils tools used here: ICS Expand RRULE, ICS to CSV, ICS to JSON, and the Cron Expression Generator.

What makes an event recurring?

A recurring event is one VEVENT with a start time and a rule. The recurrence format is defined in RFC 5545, the iCalendar standard that Google Calendar, Apple Calendar, and Outlook exports use. Five properties work together:

PropertyRoleExample
DTSTARTThe first occurrence; every later one copies its time of dayDTSTART;TZID=Europe/Istanbul:20261005T093000
RRULEThe repeating patternRRULE:FREQ=WEEKLY;BYDAY=MO,WE;COUNT=8
RDATEExtra dates added to the patternRDATE;TZID=Europe/Istanbul:20261024T093000
EXDATEDates removed from the patternEXDATE;TZID=Europe/Istanbul:20261014T093000
RECURRENCE-IDMarks a separate VEVENT, with the same UID, that replaces one occurrenceRECURRENCE-ID;TZID=Europe/Istanbul:20261019T093000

RFC 5545 builds the final list, called the recurrence set, in a fixed order: take every start produced by RRULE and RDATE, then remove every start listed in EXDATE. Duplicates count once, and EXDATE always wins over the inclusion properties.

The parts of an RRULE

An RRULE is a list of NAME=VALUE parts separated by semicolons. Only FREQ is required; every other part narrows or expands the pattern. The parts below cover nearly every rule a calendar app writes.

PartMeaningNotes from RFC 5545
FREQThe base unit: DAILY, WEEKLY, MONTHLY, YEARLY (also HOURLY, MINUTELY, SECONDLY)Required, and allowed only once
INTERVALRepeat every n unitsDefaults to 1; INTERVAL=2 with WEEKLY means every other week
COUNTStop after n occurrencesDTSTART counts as the first one
UNTILStop at this date or time, inclusiveNever together with COUNT; in UTC when DTSTART has a time zone
BYDAYDays of the week, optionally numbered2TU is the second Tuesday, -1FR the last Friday; numbers only with MONTHLY or YEARLY
BYMONTHDAYDays of the month-1 is the last day, -3 the third-to-last
BYMONTHMonths of the year, 1 to 12BYMONTH=11 keeps November
WKSTThe day a week startsDefaults to Monday; changes results for weekly rules with INTERVAL above 1
BYSETPOSPicks the nth result inside each periodFREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1 is the last weekday of the month

RRULE examples with real dates

Reading a rule is easier with dates next to it. Each row below shows a rule, its start date, and the first occurrences it produces under RFC 5545.

PatternRuleDTSTARTFirst occurrences
Every other week on Tuesday and ThursdayFREQ=WEEKLY;INTERVAL=2;BYDAY=TU,THTue 2026-10-20Oct 20, Oct 22, Nov 3, Nov 5, Nov 17, Nov 19
Second Tuesday of every monthFREQ=MONTHLY;BYDAY=2TU2026-10-13Oct 13, Nov 10, Dec 8, 2027-01-12
Last Friday of every monthFREQ=MONTHLY;BYDAY=-1FR2026-10-30Oct 30, Nov 27, Dec 25, 2027-01-29
Last day of every monthFREQ=MONTHLY;BYMONTHDAY=-12026-07-31Jul 31, Aug 31, Sep 30, Oct 31, Nov 30, Dec 31
Fourth Thursday of NovemberFREQ=YEARLY;BYMONTH=11;BYDAY=4TH2026-11-262026-11-26, 2027-11-25, 2028-11-23

To check a rule of your own, put it in an .ics file and drop it into ICS Expand RRULE, which writes every occurrence between two dates.

Surprise 1: a monthly event on the 31st skips months

A rule that repeats on the 31st does not move to the 30th in shorter months; it skips them. RFC 5545 says a recurrence that produces an invalid date, such as February 30, must be ignored and not counted.

So FREQ=MONTHLY with DTSTART on 2026-07-31 produces Jul 31, Aug 31, Oct 31, and Dec 31. September and November have no 31st, so they have no occurrence. The RFC's own example shows the same effect: FREQ=MONTHLY;BYMONTHDAY=15,30;COUNT=5 from 15 January 2007 gives January 15 and 30, February 15, and March 15 and 30.

The fix is to state what you mean. BYMONTHDAY=-1 is the last day of every month, whatever its length. The same rule explains why a yearly event that starts on 29 February appears only in leap years.

Surprise 2: EXDATE does not extend a COUNT

COUNT is applied while the rule is evaluated, and EXDATE is removed afterwards. A cancelled occurrence still uses up one of the counted slots.

Take a standup with RRULE:FREQ=WEEKLY;BYDAY=MO,WE;COUNT=8 starting Monday 2026-10-05. The rule produces Oct 5, 7, 12, 14, 19, 21, 26, and 28. Add EXDATE for Oct 14 and seven meetings remain; the series still ends on Oct 28 rather than gaining an extra date in November. Use RDATE to add a make-up meeting.

Surprise 3: a moved meeting keeps its original ID

When one occurrence moves, the calendar does not edit the rule. It writes a second VEVENT with the same UID and a RECURRENCE-ID equal to the original start of that occurrence. RFC 5545 is explicit: if a Friday meeting moves to Thursday, the RECURRENCE-ID still holds the Friday.

A tool that ignores RECURRENCE-ID can list that meeting twice: once at the time the rule gives and once at the new time. ICS Expand RRULE replaces the original occurrence with the moved one, so the output lists the meeting once, at its new time.

Surprise 4: time zones and daylight saving time

A recurring event keeps its wall-clock time, not its UTC time. RFC 5545 recommends a DTSTART with a time zone reference (TZID) so that every occurrence starts at the same local time.

A weekly 09:00 meeting in America/New_York is 13:00 UTC on 26 October 2026 and 14:00 UTC on 2 November 2026, because daylight saving time ends in the United States on 1 November 2026. The local time stays 09:00 throughout. A time without TZID and without a trailing Z is "floating": it means 09:00 wherever the attendee is.

Two practical rules follow. When DTSTART has a TZID, the RFC requires UNTIL to be written in UTC. And when you need the same meetings in another zone, convert the whole file with ICS Timezone Convert rather than editing times by hand.

Surprise 5: WKST changes results

WKST sets the first day of the week, and it matters when a weekly rule has an INTERVAL above 1 and several BYDAY values. RFC 5545 gives this example, starting Tuesday 5 August 1997:

  • FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO gives August 5, 10, 19, and 24.
  • The same rule with WKST=SU gives August 5, 17, 19, and 31.

The difference comes from which Sunday belongs to the "every other" week. If your calendar shows a fortnightly event on unexpected Sundays, check WKST first.

RRULE vs cron: which one describes your schedule?

Both describe repeating times, but they solve different problems. Cron schedules jobs on one machine; RRULE describes events people attend, with a start, an end, and exceptions.

AspectiCalendar RRULECron expression
Defined inRFC 5545POSIX crontab and its variants
Start and endDTSTART, plus COUNT or UNTILNone; runs until removed
"Last Friday of the month"BYDAY=-1FRNot in POSIX cron; some implementations add their own syntax
Every other weekINTERVAL=2No direct field
Time zonePer event, with TZIDNot part of the POSIX format; each scheduler has its own setting
ExceptionsEXDATE, RDATE, RECURRENCE-IDNone
Day-of-month and day-of-week togetherBoth must matchEither may match (POSIX)

The last row trips people up. The POSIX crontab specification says that when both the day-of-month and day-of-week fields are restricted, a day matching either one runs the job. In an RRULE, FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13 means only Fridays that fall on the 13th. Build and check cron schedules in the Cron Expression Generator.

How to see every occurrence of a recurring event

Expanding a rule turns one recurring event into one event per date. On the ICS Expand RRULE page, the calendar is read in your browser and nothing is uploaded.

Step 1Export the calendar

Export the calendar as an .ics file. In Google Calendar, use Settings, then Import & export, then Export; in Apple Calendar, File, then Export; in Outlook, File, then Save Calendar.

Step 2Drop the file and set the window

Drop the .ics file on the page and set From and To. A rule without COUNT or UNTIL repeats forever, so the window decides which occurrences are written. Each series stops at a cap of 366 occurrences by default, adjustable up to 2000.

Step 3Read the notes

The notes name every series that was cut at the cap, every monthly series that skips short months, and every rule part the page does not expand. It expands DAILY, WEEKLY, MONTHLY, and YEARLY rules with INTERVAL, COUNT, UNTIL, BYDAY (including ordinals), BYMONTHDAY, BYMONTH, and WKST, plus EXDATE, RDATE, and RECURRENCE-ID. A rule that uses BYSETPOS, BYYEARDAY, BYWEEKNO, BYHOUR, BYMINUTE, BYSECOND, or an hourly or finer frequency keeps its first occurrence only.

Step 4Download the format you need

Choose .ics to import into another calendar, JSON for a script, or CSV for a spreadsheet. Each occurrence gets its own UID, so import the expanded .ics into a new, separate calendar; importing it next to the original series creates a second copy of every meeting.

If you only need the rule as text, ICS to CSV and ICS to JSON keep one row or object per event with the RRULE in its own field. To keep only some events before expanding, use ICS Filter.

FAQ

What does RRULE stand for?

RRULE is the "recurrence rule" property of the iCalendar format, defined in RFC 5545. Its value lists the frequency and the parts that pick dates, such as FREQ=MONTHLY;BYDAY=2TU.

Does DTSTART count as an occurrence?

Yes. RFC 5545 says the DTSTART value always counts as the first occurrence, including toward COUNT. A rule with COUNT=10 produces ten dates in total, the start included.

How do I make an event repeat on the last day of every month?

Use FREQ=MONTHLY;BYMONTHDAY=-1. A rule that repeats on the 31st skips months with fewer days, because RFC 5545 ignores invalid dates instead of moving them.

What is the difference between EXDATE and RECURRENCE-ID?

EXDATE removes an occurrence from the series. RECURRENCE-ID belongs to a separate event with the same UID and replaces one occurrence with new details, such as a different time or room.

Can I use COUNT and UNTIL together?

No. RFC 5545 allows either COUNT or UNTIL in one rule, not both. Without either, the rule repeats forever.

Is ICS Expand RRULE free, and is my calendar uploaded?

It is free and needs no signup. On the tool page the .ics file is read and expanded in your browser, and the calendar text is not uploaded.

Tools used in this post

Sources