How to Merge Multiple Calendars into a Single ICS File

Why concatenating .ics files by hand breaks the calendar, how duplicate UIDs and mismatched VTIMEZONE blocks behave when you combine exports, and what to check before importing a merged file.

Merging calendars means producing one valid VCALENDAR object that contains every VEVENT from every source file. That is not the same as pasting the files together: an ICS file has exactly one BEGIN:VCALENDAR / END:VCALENDAR wrapper, one PRODID, one VERSION, and a set of VTIMEZONE definitions that the events reference by name. Concatenating two exports gives you a file with two wrappers, which most parsers either reject outright or read only up to the first END:VCALENDAR — silently dropping everything after it. This guide covers what actually has to happen during a merge, what to do about duplicate events and timezone collisions, and what to verify once the merged file exists.

Why you cannot just concatenate the files

RFC 5545 defines an iCalendar stream as one or more iCalendar objects, but it also says a conforming application only has to handle the first one. In practice, Google Calendar, Apple Calendar and Outlook all behave as if there is exactly one. Run `cat work.ics personal.ics > all.ics` and you get a file whose second half is either invisible or triggers a parse error, depending on which importer reads it.

Even if a parser were tolerant, three properties would still conflict:

  • PRODID identifies the software that produced the calendar. It is required and must appear exactly once per VCALENDAR. Two PRODID lines in one object is invalid.
  • VERSION declares the iCalendar version (always 2.0 in practice) and is likewise required once. A duplicate is a hard validation error.
  • CALSCALE, METHOD and X- properties such as X-WR-CALNAME and X-WR-TIMEZONE are calendar-level. If two files disagree — one says X-WR-TIMEZONE:Europe/Madrid, the other America/New_York — the importer picks one arbitrarily and floating-time events shift accordingly.

A correct merge takes the VEVENT, VTODO and VJOURNAL components out of every source, keeps one calendar-level header, collects the union of the VTIMEZONE blocks the events actually reference, and writes a single object. Our Merge ICS Files tool does that in the browser: drop in as many files as you want and you get one clean calendar back, with nothing uploaded to a server.

Duplicate events: the UID problem

UID is the globally unique identifier of an event. Two components with the same UID are not two events — they are the same event, and the one with the higher SEQUENCE number (or the later DTSTAMP, when SEQUENCE ties) is the current version. This matters most in the case that trips people up constantly: exporting the same calendar twice, a few weeks apart, and merging both exports.

The events in those two files share UIDs. What happens on import depends on the app. Google Calendar treats the UID as authoritative and updates the existing entry rather than adding a second one, so a naive merge quietly loses whichever copy loses the tie. Apple Calendar tends to keep both if the DTSTART differs. Outlook is the least predictable and will often create visible duplicates on the same day.

Same event, different UID

The reverse case is just as common. If you were invited to a meeting on two accounts, or the same event was created independently in two calendars, the UIDs differ even though the title, date and location match. No importer will catch that, because by the spec they are genuinely different events. Deduplication has to compare SUMMARY plus DTSTART, not just UID. Our Remove Duplicate Events tool matches on both criteria, and running it after a merge is usually the right order: merge first so you have every candidate in one file, then deduplicate.

Recurring events and RECURRENCE-ID

A modified instance of a recurring series is stored as a separate VEVENT that carries the same UID as the master plus a RECURRENCE-ID pointing at the original occurrence. If a merge drops one of those overrides, or keeps the override but loses the master, the series either loses its exception or fails to expand at all. Never deduplicate on UID alone when the file contains recurring events — components sharing a UID but differing in RECURRENCE-ID must all survive.

What happens to timezones when sources disagree

Each timed event references a timezone through the TZID parameter on DTSTART and DTEND, and the calendar is supposed to carry a matching VTIMEZONE component defining that identifier. Three failure modes show up when merging files from different origins.

  • Missing definition: an event says TZID=Europe/Madrid but no VTIMEZONE block for Europe/Madrid made it into the merged file. Strict parsers reject the event; lenient ones fall back to the system timezone, which is right on your laptop and wrong for anyone else.
  • Conflicting definitions: two files each define a VTIMEZONE for the same TZID with different DST rules, usually because they were exported years apart or by different software. Only one can survive, and events from the other file inherit rules they were not written against.
  • Non-IANA identifiers: Microsoft exports frequently use names like "Romance Standard Time" or "Eastern Standard Time" instead of Europe/Madrid or America/New_York. Merge a Microsoft export with a Google export and half the events use IANA names and half do not. Google Calendar rejects the Windows names outright.

There is also UTC and floating time. Events written as DTSTART:20260601T090000Z are absolute and merge without ambiguity. Events written as DTSTART:20260601T090000, with no Z and no TZID, are floating: they mean 09:00 in whatever timezone the reading device happens to be in. Mixing floating events from one source with zoned events from another produces a calendar that looks correct to the person who built it and wrong to everyone else. If any source uses floating times, normalize them before merging — the Fix / Clean ICS tool rewrites Windows timezone names to IANA identifiers and anchors floating times to an explicit zone.

Real cases where merging is the right move

Work and personal in one view

Exporting the work calendar and the personal calendar and merging them gives you a single file you can import into a phone that only syncs one account, or hand to a scheduling tool that accepts one upload. Watch for the UID overlap if both accounts received the same meeting invitation.

Consolidating team calendars

Five people export their calendars, you merge them, and you have a combined availability file to load into a planning tool or print. Because each person exports from their own account, UIDs are distinct even for the meeting they all attended — so deduplicate by title and start time, not by UID, or the shared meeting appears five times.

Building a public events calendar

Conference tracks, league fixtures and course schedules are usually maintained separately and published together. Merge the per-track files into one calendar, set a single meaningful X-WR-CALNAME, and host the result at a stable URL so people can subscribe rather than import. Subscribers re-fetch the file, so keep the UIDs stable between publications: change them and every subscriber gets a fresh set of duplicated events on the next sync.

What to check after merging

  • Event count. Count BEGIN:VEVENT lines in the sources and in the result. If the merged total is lower than the sum, something was dropped or deduplicated — know which.
  • One header. Exactly one BEGIN:VCALENDAR, one END:VCALENDAR, one PRODID, one VERSION.
  • Timezone coverage. Every distinct TZID used by an event has a matching VTIMEZONE component in the file.
  • Recurring series. Open the file and confirm the RRULE lines survived, and that any RECURRENCE-ID overrides are still paired with their master event.
  • A validation pass. Run the merged file through Validate ICS before importing; it flags missing required properties, broken RRULE syntax and orphaned timezone references with line numbers.
  • A test import. Import into a throwaway calendar first. Deleting one imported calendar is easy; unpicking 300 events from your main one is not.

Bottom line

Merging calendars is a structural operation, not a text operation. The file needs one wrapper and one header, the timezone definitions have to follow the events that reference them, and duplicates have to be resolved on the right key — UID for re-exports of the same calendar, title plus start time for the same event created in different accounts. Get those three things right and the merged file imports cleanly everywhere.