Skip to content

Convert

ICS Expand RRULE

ICS Expand RRULE

Beta

Turn the recurring events of an .ics calendar into one event per occurrence between two dates, so a spreadsheet, a CRM or an importer that cannot read RRULE still sees every meeting. Download the result as .ics, JSON or CSV. On this page, the calendar is read in your browser and nothing is uploaded.

Use via API
  • Free, no sign-up
  • REST + MCP
  • Updated
  • Reviewed by Olgun Ozoktas

At most 2000.

Output format

Each occurrence between From and To becomes its own VEVENT, with no RRULE left in the file.

Drop one .ics file hereor click to choose a calendar export
Expanded calendar

The expanded calendar will appear here.

How to Expand an RRULE Calendar

  1. Export the calendar as .ics

    In Google Calendar, Settings > Import & export > Export. Apple Calendar: File > Export. Outlook: File > Save Calendar.
  2. Drop the .ics file on the page, or click to choose it

  3. Set From and To

    Only occurrences that touch those days are written. Keep the cap per series unless you need more than 366 occurrences of one series.
  4. Choose .ics to import the result into another calendar, JSON for a script, or CSV for a spreadsheet, then download

Common Use Cases

Spreadsheets

one row per standup, class or shift for the quarter, ready to sum hours or attendance.

CRM and billing imports that accept single events only

Legacy calendar importers that ignore RRULE and keep only the first occurrence

Checking a rule

see which dates FREQ=MONTHLY;BYDAY=-1FR really produces before you send the invite.

Audits

list every occurrence of a series between two dates, with the moved ones in their new place.

Why Expand Recurring Events?

A calendar export stores a weekly meeting once, with a rule such as RRULE:FREQ=WEEKLY;BYDAY=MO that says when it repeats. A calendar app reads that rule; a spreadsheet, a CRM import, a billing script or an older importer does not, and shows one row where there were fifty meetings. Expanding the rule means working out every date it produces, removing the dates the file excludes, and swapping in the occurrences that were moved. This page does that for the window you choose and writes each occurrence as its own event, so the next tool sees exactly the meetings that happen.

The expansion follows RFC 5545 section 3.3.10 for the parts most calendars use: FREQ=DAILY, WEEKLY, MONTHLY and YEARLY; INTERVAL; COUNT and UNTIL; BYDAY, including ordinals such as 2TU for the second Tuesday and -1FR for the last Friday in monthly and yearly rules; BYMONTHDAY, where a negative day counts from the end of the month; BYMONTH; and WKST. DTSTART is always the first occurrence and counts towards COUNT, even when it falls before the window. EXDATE removes occurrences, RDATE adds them, and a component with the same UID and a RECURRENCE-ID replaces the occurrence it names with its own time and details.

The arithmetic runs on the wall-clock time the file states. A series at 09:30 Europe/Istanbul is written at 09:30 Europe/Istanbul on every date, across daylight-saving changes, which is how RFC 5545 defines a recurrence. The output keeps the TZID or the trailing Z of each value. One value is read across zones: a UNTIL, EXDATE, RDATE or RECURRENCE-ID written in UTC for a series in a named zone is read in that zone, so it matches the occurrence it names.

Each series stops at the cap you set, 366 occurrences by default and at most 2000, and the whole output stops at 20,000 events. When a series is cut, the notes say which one. Single events that touch the window are copied once, unchanged. The JSON output has the same fields as ICS to JSON, and the CSV output has the same columns as the spreadsheet layout of ICS to CSV.

How it compares

Google Calendar and Outlook keep a recurring meeting as one event with a rule, and their exports do the same, so a converter that reads the export row by row sees one meeting. Writing a script with an RRULE library works, but means installing it, reading the time zones right and handling moved and cancelled occurrences yourself. This page reads the standard format, applies the exceptions the file holds, and writes plain events in the window you choose, without a library to install.

Expansion Tips

  • A monthly rule on the 29th, 30th or 31st skips the months that do not have that day, as RFC 5545 says. The notes name each series this affects.
  • Each occurrence gets its own UID: the series UID plus the occurrence start. Importing the result next to the original series creates duplicates, so import it into a separate calendar.
  • EXDATE removes an occurrence; a component with a RECURRENCE-ID replaces one. Both are applied before anything is written.
  • Times keep their TZID, or their trailing Z. Nothing is converted between zones.
  • A rule that uses BYSETPOS, BYYEARDAY, BYWEEKNO, BYHOUR, BYMINUTE, BYSECOND or an hourly or finer frequency keeps its first occurrence only, and the notes say so.

Frequently Asked Questions

What does expanding an RRULE mean?

Expanding an RRULE turns one recurring event into one event per occurrence. A weekly standup with RRULE:FREQ=WEEKLY;BYDAY=MO becomes a separate VEVENT for each Monday between the two dates you choose, with its own DTSTART and DTEND and no RRULE. Exception dates (EXDATE) are left out and moved occurrences (RECURRENCE-ID) are written at their new time, so the result lists the meetings that really happen.

Which recurrence rules are supported?

FREQ=DAILY, WEEKLY, MONTHLY and YEARLY, with INTERVAL, COUNT, UNTIL, BYDAY (including ordinals such as 2TU and -1FR), BYMONTHDAY (negative values count from the month's end), BYMONTH and WKST, plus EXDATE, RDATE and RECURRENCE-ID overrides. A rule with BYSETPOS, BYYEARDAY, BYWEEKNO, BYHOUR, BYMINUTE, BYSECOND or an HOURLY, MINUTELY or SECONDLY frequency keeps only its first occurrence, and the notes name the part that was not expanded.

Why do I have to set a date range?

Because a rule without COUNT or UNTIL repeats forever, and even a bounded one can produce thousands of events nobody needs. The range From to To decides which occurrences are written: an occurrence is kept when any part of it falls on those days. The default is today through 90 days from today. The range is read in each event's own zone, as the file states it.

What is the max occurrences cap?

It is a safety limit per series: 366 occurrences by default, and at most 2000. When a series has more occurrences in the range than the cap, the first ones up to the cap are written and the notes name the series. The whole output also stops at 20,000 events. Choose a shorter range when you hit either limit.

Why are some months missing from a monthly event?

A monthly event that repeats on the 29th, 30th or 31st skips every month that does not have that day. RFC 5545 says an invalid date such as 31 November is ignored, not moved to the 30th. The notes name each series this affects. To get the last day of every month, the rule has to say BYMONTHDAY=-1.

How are time zones handled?

Every occurrence keeps the TZID or the trailing Z of the series, and the clock time stays as the file states it, so a 09:30 Europe/Istanbul meeting is 09:30 Europe/Istanbul on every date, across daylight-saving changes. Nothing is converted. The one cross-zone reading: a UNTIL, EXDATE, RDATE or RECURRENCE-ID written in UTC for a zoned series is read in the series' zone, using the browser's zone rules, so it matches the right occurrence. If the zone name is one the browser does not know, the value is compared as written and the notes say so.

What happens to moved and cancelled occurrences?

A moved occurrence, written in the file as a component with the series UID and a RECURRENCE-ID, replaces the occurrence it names and is written at its own time with its own details. With Skip cancelled series on, which is the default, a series with STATUS:CANCELLED, a cancelled occurrence and a cancelled single event are left out and counted. Turn it off to write them with their STATUS:CANCELLED line.

Can I import the expanded calendar back into Google Calendar or Outlook?

Yes. The .ics output is a complete calendar with one VEVENT per occurrence and the VTIMEZONE blocks it uses. Each occurrence has its own UID, the series UID followed by the occurrence start, so importing it next to the original series adds a second copy of every meeting. Import it into a new, separate calendar when you want both.

What do the JSON and CSV outputs contain?

The JSON output is an array with one object per event, in the same shape as ICS to JSON: uid, summary, description, location, start, end, all_day, timezone, status and the rest, with rrule empty. The CSV output has the spreadsheet columns of ICS to CSV: summary, start, end, all_day, timezone, location, description, organizer, status, categories, rrule and uid. Both are sorted by start time.

Is my calendar uploaded?

Not on this page. The file is read with the browser's File API and expanded in the tab, and the page makes no request with the calendar text, which you can confirm in the browser's network panel. The page never fetches a calendar URL either. The REST API and MCP tool are a separate path: a call sends the input you include to FindUtils over HTTPS, where it is processed and the result returned. FindUtils does not store that input, and logs the request without its content.

Can I expand recurring events from a script?

Yes. The same code runs on the FindUtils REST API and MCP server as the ics_expand_rrule tool. Send the .ics text as input with from and to as YYYY-MM-DD, and optionally max_occurrences, format (ics, json or csv) and skip_cancelled; read the result from output, with the counts and notes beside it.

Rate This Tool

0/1000

Get Weekly Tools

Suggest a Tool