---
title: "ICS Expand RRULE"
description: "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."
url: https://findutils.com/convert/ics-expand-rrule/
category: convert
---

# ICS Expand RRULE

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 this tool:** [ICS Expand RRULE](https://findutils.com/convert/ics-expand-rrule/)

## Programmatic access

- REST id `ics-expand-rrule`: POST https://api.findutils.com/api/tools/ics-expand-rrule/execute (reference: https://findutils.com/api/ics-expand-rrule/)
- MCP tool `ics_expand_rrule` on https://mcp.findutils.com (reference: https://findutils.com/mcp/ics-expand-rrule/)
- Data: The REST API and the MCP tool are a separate path from this page: 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.

## 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.

## 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](https://findutils.com/convert/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](https://findutils.com/convert/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.

## Related Tools

- [ICS to CSV](https://findutils.com/convert/ics-to-csv/)
- [ICS to JSON](https://findutils.com/convert/ics-to-json/)
- [ICS Filter](https://findutils.com/convert/ics-filter/)
- [ICS Diff](https://findutils.com/convert/ics-diff/)
- [Merge ICS](https://findutils.com/convert/merge-ics/)
- [Split ICS](https://findutils.com/convert/split-ics/)
- [CSV to ICS](https://findutils.com/convert/csv-to-ics/)
