Calendar (.ics) File Structure
This article describes how iCalendar data is stored on a CalDAV server. To fond how to parse and generate iCalendar data read articles in the Using IT Hit Collab Library API section.
Event/To-do Components and Instances
Every calendar file stored on a CalDAV server contains a single iCalendar object with a single event or to-do defined inside it. Every event/to-do object consists of one or more event or to-do components. It also typically contains one or more time zone component.
Recurring event/to-do expands to event/to-do instances. You can see event/to-do instances when calendar is displayed in your client application. Below is an iPhone Calendar application with a recurring event that has 4 instances Monday through Thursday:
Note that it consists of a single event component. Below you can see this event in iCalendar format, you will find a single VEVENT definition in it:
BEGIN:VCALENDAR … BEGIN:VEVENT CREATED:20151219T021727Z DTEND;TZID=America/Toronto:20170515T110000 DTSTAMP:20151219T021727Z DTSTART;TZID=America/Toronto:20170515T100000 LAST-MODIFIED:20151219T021727Z RRULE:FREQ=DAILY;UNTIL=20170519T035959Z SEQUENCE:0 SUMMARY:Meeting TRANSP:OPAQUE UID:21B97459-D97B-4B23-AF2A-E2759745C299 END:VEVENT END:VCALENDAR
A non-recurring event or recurring event that does not have overridden instances consists of a single component. However when you update an event instance of a recurring event, more components are created, see below.
Do not confuse the VCALENDAR object that you will find inside a .ics file with actual calendar. The calendar is a calendar folder and is represented by ICalendarFolder interface. Every calendar file (.ics file) on a CalDAV server stores a single event or to-do, even though the info about event/to-do is wrapped in “VCALENDAR” block. The .ics file is represented by ICalendarFile interface. You can find more about calendar folders and calendar files in the Creating CalDAV Server article.
What Happens when You Update an Instance of a Recurring Event?
When you change a separate event instance in your CalDAV client application, for example when you update the time or summary for a particular day, the client submits to the server the updated iCalendar object with a new component added to the event. This new component contains information about updated event instance and has a RECURRENCE-ID property that stores date and time of the event instance. Here is the example of the above event with the last instance summary being changed to “Final Meeting” and start time being set to 11:00AM:
Here is its representation in iCalendar format. Note the 2 VEVENT components stored in it – the first one contains a recurring event description and the second one - event instance that was overridden by the client ("Final Meeting").
BEGIN:VCALENDAR … BEGIN:VEVENT CREATED:20151219T021727Z DTEND;TZID=America/Toronto:20170515T110000 DTSTAMP:20151219T022011Z DTSTART;TZID=America/Toronto:20170515T100000 LAST-MODIFIED:20151219T021727Z RRULE:FREQ=DAILY;UNTIL=20170519T035959Z SEQUENCE:0 SUMMARY:Meeting TRANSP:OPAQUE UID:21B97459-D97B-4B23-AF2A-E2759745C299 END:VEVENT BEGIN:VEVENT CREATED:20151219T022011Z DTEND;TZID=America/Toronto:20170518T120000 DTSTAMP:20151219T022011Z DTSTART;TZID=America/Toronto:20170518T110000 LAST-MODIFIED:20151219T022011Z RECURRENCE-ID;TZID=America/Toronto:20170518T100000 SEQUENCE:0 SUMMARY:Final Meeting TRANSP:OPAQUE UID:21B97459-D97B-4B23-AF2A-E2759745C299 END:VEVENT END:VCALENDAR
What Happens when You Delete a Recurring Event Instance?
When you delete the event instance, the client application will add an EXDATE property for every event instance that was deleted with date and time of the instance that should not occur. The EXDATE property is called exception dates and can contain more than one exception date as every EXDATE property value. Below you can see an example of the same event with 2 instances, on Tuesday and Wednesday, being deleted:
The above event with EXDATE in iCalendar format:
BEGIN:VCALENDAR … BEGIN:VEVENT CREATED:20151219T021727Z DTEND;TZID=America/Toronto:20170515T110000 DTSTAMP:20151219T022251Z DTSTART;TZID=America/Toronto:20170515T100000 EXDATE;TZID=America/Toronto:20170516T100000 EXDATE;TZID=America/Toronto:20170517T100000 LAST-MODIFIED:20151219T022251Z RRULE:FREQ=DAILY;UNTIL=20170519T035959Z SEQUENCE:0 SUMMARY:Meeting TRANSP:OPAQUE UID:21B97459-D97B-4B23-AF2A-E2759745C299 END:VEVENT BEGIN:VEVENT CREATED:20151219T022011Z DTEND;TZID=America/Toronto:20170518T120000 DTSTAMP:20151219T022251Z DTSTART;TZID=America/Toronto:20170518T110000 LAST-MODIFIED:20151219T022011Z RECURRENCE-ID;TZID=America/Toronto:20170518T100000 SEQUENCE:0 SUMMARY:Final Meeting TRANSP:OPAQUE UID:21B97459-D97B-4B23-AF2A-E2759745C299 END:VEVENT END:VCALENDAR
UID and RECURRENCE-ID
All components within an event/to-do share the same UID. Typically this UID is also used as a calendar (.ics) file name.
Important! iOS Calendar UID is case sensitive, it requires uppercase UID only. The uppercase and lowercase UID are treated as different ids.
In addition to UID every overridden instance has its own RECURRENCE-ID which identifies the instance that this component overrides. As you can see in the above example the RECURRENCE-ID is a date and time of the specific instance in the recurrence set.