You have almost certainly received an ICS file. It arrives attached to a meeting invitation, or downloads when you click "Add to Calendar" on a ticketing page. You double-click it, your calendar app opens, and an event appears. Most of the time it just works.
When it doesn't, ICS files become genuinely confusing. The event lands an hour off. Only the first date of a weekly meeting shows up. Nothing happens at all when you tap the attachment on your phone. Understanding what is actually inside the file makes all of those failures much easier to diagnose.
What follows is the format itself, where it came from, what every important line inside a file does, and where it falls down.
An ICS file is a plain text file describing calendar data: events, to-dos, free/busy information, journal entries and timezone definitions. The .ics extension stands for iCalendar, the specification the file follows. Open one in a text editor and you see readable key/value pairs, not binary gibberish.
That is the most useful thing to know about the ICS file format. It is text. You can read it, diff it, grep it, and fix it by hand. When someone says "the event imported at the wrong time", the answer is almost always visible in the raw file.
The format is registered under the MIME type text/calendar, which is how mail clients and servers know to hand the file to a calendar app rather than a text editor.
Every ICS file is built from nested components. Each component starts with BEGIN: and ends with a matching END:. The outermost is always VCALENDAR, and inside it you find one or more VEVENT, VTODO, VJOURNAL, VFREEBUSY or VTIMEZONE components.
Here is an annotated example of a small but complete file:
BEGIN:VCALENDAR VERSION:2.0 PRODID:-//AddCal//EN CALSCALE:GREGORIAN METHOD:PUBLISH BEGIN:VTIMEZONE TZID:Europe/London BEGIN:STANDARD DTSTART:19701025T020000 TZOFFSETFROM:+0100 TZOFFSETTO:+0000 RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU END:STANDARD BEGIN:DAYLIGHT DTSTART:19700329T010000 TZOFFSETFROM:+0000 TZOFFSETTO:+0100 RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU END:DAYLIGHT END:VTIMEZONE BEGIN:VEVENT UID:9f2c1a70-team-standup@addcal.co DTSTAMP:20260114T093000Z DTSTART;TZID=Europe/London:20260119T093000 DTEND;TZID=Europe/London:20260119T094500 SUMMARY:Weekly product standup LOCATION:Meeting Room 2\, Level 4 DESCRIPTION:Agenda in the shared doc. Bring blockers. RRULE:FREQ=WEEKLY;BYDAY=MO;COUNT=12 STATUS:CONFIRMED SEQUENCE:0 BEGIN:VALARM TRIGGER:-PT10M ACTION:DISPLAY DESCRIPTION:Standup in 10 minutes END:VALARM END:VEVENT END:VCALENDAR
Every line in that file earns its place. Working through the important ones:
VERSION:2.0 is required and means "this is iCalendar as defined by RFC 5545". There is no version 3.PRODID identifies the software that produced the file. It is required, and it is invaluable when debugging: it tells you which system generated the ICS file you are staring at.CALSCALE:GREGORIAN is the default and usually omitted.METHOD is optional and hugely consequential. More on that below.UID is the unique identifier for the event. It must be globally unique and it must stay stable across updates. This is the single most important property in the file for anything beyond a one-off import: when you re-send an updated ICS file with the same UID, a compliant client updates the existing event rather than creating a second copy. When generators lazily emit a fresh random UID every time, you get duplicates.
DTSTAMP is when this particular representation of the event was created, always in UTC. Paired with SEQUENCE, it lets clients work out which of two versions of the same UID is newer.
DTSTART and DTEND are the start and end. They come in three flavours, and mixing them up is responsible for most timezone bugs:
DTSTART:20260119T093000Z. Unambiguous, but loses the original local time.DTSTART;TZID=Europe/London:20260119T093000. Requires a matching VTIMEZONE in the same file. This is the correct choice for recurring events, because "every Monday at 09:30" should stay at 09:30 after the clocks change.All-day events use a fourth form: DTSTART;VALUE=DATE:20260119. The critical rule here is that DTEND is exclusive. A single-day event on 19 January has DTSTART;VALUE=DATE:20260119 and DTEND;VALUE=DATE:20260120. Getting this wrong is why all-day events sometimes render as one day short or one day long.
SUMMARY is the event title. LOCATION is a free-text location string. DESCRIPTION is the body. All three are plain text with a specific escaping rule: commas, semicolons and backslashes must be escaped with a backslash, and newlines are written as a literal \n. That is why the example above has Meeting Room 2\, Level 4.
RRULE defines recurrence. It is a compact grammar of its own: FREQ (DAILY, WEEKLY, MONTHLY, YEARLY), plus modifiers such as INTERVAL, BYDAY, BYMONTHDAY, BYSETPOS, and a terminator of either COUNT or UNTIL. "Last Friday of every month" is FREQ=MONTHLY;BYDAY=-1FR. Individual instances can be removed with EXDATE or overridden with a second VEVENT carrying the same UID plus a RECURRENCE-ID naming the instance being replaced.
VALARM is a nested component describing a reminder, where TRIGGER:-PT10M means ten minutes before the start. Most modern clients ignore alarms in imported files and apply the user's own default instead, which is sensible: nobody wants an emailed invitation setting alarms on their phone.
ATTENDEE and ORGANIZER carry email addresses and participation status, and only matter for actual invitations. An attendee line typically looks like ATTENDEE;CN=Sam Lee;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:sam@example.com.
RFC 5545 says no line may exceed 75 octets, excluding the line break. Longer lines are "folded": broken at any point and continued on the next line, which must begin with a single space or tab. Parsers unfold by removing the CRLF and the following whitespace character.
Two practical consequences. First, the limit is in octets, not characters, so a fold must not split a multi-byte UTF-8 sequence. Second, a naive line-by-line parser will mangle long descriptions and URLs: unfold first, then parse. To inspect a file without writing code, the free ICS viewer unfolds and pretty-prints any .ics you drop into it. Lines are also required to end with CRLF, not bare LF, and strict parsers do enforce it.
This one property changes how the entire file is treated, and it is the difference between "here is an event you might like" and "you are invited to this meeting, please reply".
METHOD:PUBLISH means the file is informational. Clients import it as a plain event. No RSVP buttons appear, no reply email is generated. This is what you want for a public event, a webinar listing, or anything you are sharing broadly.METHOD:REQUEST means the file is a scheduling request, governed by iTIP. Combined with ORGANIZER and ATTENDEE lines, clients render Accept / Tentative / Decline buttons and send a METHOD:REPLY back to the organiser. Outlook in particular treats these very differently from published events.METHOD:CANCEL withdraws a previously sent event, matched on UID.A common mistake is sending a marketing event to a thousand people with METHOD:REQUEST and every recipient as an attendee. Everyone then sees the full recipient list, and every RSVP fires an email at you. Use PUBLISH for broadcast, REQUEST for real meetings.
invite.ics and rendered inline by Gmail, Outlook and Apple Mail..ics alongside deep links into Google and Outlook. If you are building one, the add to calendar link generator produces the whole set from one form.The format is thirty years old and it shows in a few places.
An attached file is a snapshot, not a subscription. Once someone has imported your ICS attachment, you have no way to change it. Move the venue and every recipient is still holding the old address. You can re-send with the same UID and a higher SEQUENCE, but only if you know everyone's email address and only if their client honours the update, which is inconsistent in practice. For anything that might change, a link that resolves live beats a file every time. That is the core argument for add to calendar links over raw file downloads.
Mobile clients handle attachments badly. Tapping a .ics file on Android often opens a file manager rather than a calendar. On iOS it usually works from Mail but not from every third-party app. Neither Google Calendar's mobile app nor most webmail clients offer a true import path.
Rich content does not survive. DESCRIPTION is plain text. Some clients read the non-standard X-ALT-DESC;FMTTYPE=text/html property, most do not. Assume formatting will be flattened.
Recurrence support is uneven. Every client implements RRULE, but complex rules using BYSETPOS or unusual WKST values expand differently across implementations. Timezone handling around DST transitions is another source of one-hour drift.
Double-clicking is usually enough. On Windows the file opens in Outlook or the Calendar app, on macOS in Apple Calendar, and on most Linux desktops in whichever application is registered for text/calendar. The event appears in a preview window and you confirm before it is saved.
To read the file rather than import it, open it in any text editor. An ICS file is plain text, so Notepad, TextEdit or VS Code will all show you the raw properties. This is the fastest way to diagnose a bad import: the wrong time, a missing recurrence or a duplicate is nearly always visible in the source. If you would rather see it parsed into labelled fields, the free ICS viewer unfolds the file and lists every property without uploading it anywhere.
Platform-specific import paths differ more than you would expect, particularly on mobile. The walkthroughs for Google Calendar, iPhone and Apple Calendar and Outlook cover the quirks of each.
Every major calendar application can export one, though the menus are less consistent than you would hope.
Google Calendar exports from a web browser only, not the mobile app. Settings, then Import & export, then Export downloads a ZIP containing one .ics per calendar. To export a single calendar as a bare .ics instead, hover it in the calendar list, then More, then Settings and sharing, then Export calendar.
Apple Calendar on macOS exports a whole calendar, not an individual event. Select the calendar's name in the calendar list, then File, then Export, then Export. If the list is hidden, View, then Show Calendar List. Avoid File, then Export, then Calendar Archive: that produces an .icbu archive of every calendar rather than an .ics, which is a common trap.
Outlook depends on which Outlook you are running. In classic Outlook for Windows, File, then Save Calendar, then More Options lets you set the date range and level of detail before saving an .ics. Outlook on the web has no equivalent save command: instead use Settings, then Shared calendars, then Publish a calendar, which gives you an ICS link rather than a file. In the new Outlook for Windows the reliable route is still classic Outlook, reachable from the Help menu where the switch is offered.
Writing a file by hand is also entirely reasonable, because the format is text. The minimum viable file is a VCALENDAR wrapper containing a single VEVENT with UID, DTSTAMP, DTSTART and SUMMARY. Everything else is optional. The annotated example above is a complete, valid file you can copy and edit.
Two things to get right if you do hand-roll it: keep the UID stable across updates so clients update rather than duplicate, and either use UTC with a trailing Z or include a matching VTIMEZONE for any TZID you reference. The ICS generator handles both automatically if you would rather fill in a form, and for anything you might need to change later, an add to calendar link stays editable after people have added it.
ICS is short for iCalendar, the specification the file implements. The format is also referred to as iCal or by its MIME type text/calendar. All of these describe the same RFC 5545 format.
Effectively yes, though the terms get muddled. ICS is the file extension, iCalendar is the format, and iCal was the name of Apple's calendar application before it was renamed to Calendar in 2012. People use iCal to mean any of the three. Webcal is genuinely different: it is a URL scheme for subscribing to a feed rather than a file format. ICS vs iCal vs webcal covers the distinction in full.
Yes. A single VCALENDAR can hold any number of VEVENT components, which is how a full calendar export fits into one file. Note that Google Calendar caps imports at 1MB, so large exports sometimes need splitting before they will upload.
Almost always a timezone problem. Either DTSTART was written in floating local time with no timezone reference, or it used a TZID whose matching VTIMEZONE block is missing from the file. Open the file and check whether DTSTART ends in Z, carries a TZID, or has neither.
Because the UID changed. A compliant client matches on UID and updates the existing event when it recognises one. Generators that emit a fresh random UID on every export defeat that, so each import creates another copy.
The format is inert plain text with no scripting, so opening one carries little risk in itself. Ordinary caution applies to the contents: an event DESCRIPTION or URL can link anywhere, and calendar invitations from unknown senders are a known phishing vector. Read it in a text editor first if you are unsure.
iCalendar started life as RFC 2445 in 1998, produced by an IETF working group with input from Lotus, Microsoft and Netscape. The goal was interoperability: a way for calendar systems from different vendors to exchange scheduling data without a shared server.
RFC 2445 was replaced by RFC 5545 in 2009, which is the version everything implements today. It tightened ambiguous language, clarified recurrence behaviour and fixed a number of edge cases. Later documents extended it: RFC 5546 defines iTIP (invitations, replies and cancellations), RFC 6047 defines iMIP (iTIP carried over email), and RFC 7986 added properties such as COLOR, IMAGE and CONFERENCE.
In practice, "an ICS file" means "a file following RFC 5545", with a scattering of non-standard X- properties vendors have bolted on. Apple adds X-APPLE-STRUCTURED-LOCATION, Microsoft a small army of X-MICROSOFT- properties. Everyone else ignores them, which is what the spec tells parsers to do with properties they do not recognise.
If you are trying to get a specific file into a specific app, these walkthroughs cover the platform quirks in detail:
And if you want to look inside a file right now, drop it into the ICS viewer to see every property parsed out, or use the ICS to CSV converter if you would rather have the events in a spreadsheet.
Once you can read the format, most calendar problems stop being mysterious. Wrong time? Check DTSTART and whether a VTIMEZONE is present. Duplicates? Check UID stability. Only one instance imported? Check the RRULE. The answer is nearly always sitting in the text. And if you would rather not hand-write any of it, the free ICS generator builds a spec-compliant file from a simple form, or you can share an event as a link that stays editable from addcal.co.
Last updated on July 25, 2026