转载:http://blogs.msdn.com/sharepoint/archive/2007/05/14/understanding-the-sharepoint-calendar-and-how-to-export-it-to-ical-format.aspx
Introduction
One of the challenges of accessing SharePoint calendars via the object model is that there are so many different types of events – normal events, all-day events, recurring events, recurrence exceptions, and deleted instances – and they all look more or less the same! In this post, I will shed some light on how to work with calendar events, describe the subtleties of recurrences, and put that knowledge to work exporting events to the RFC 2445 iCalendar format.
First, before we can do anything with a Calendar list, we need a way to distinguish between different event types.
Distinguishing between calendar item types
Calendar items in SharePoint fall into several categories. Single events are those which don’t repeat and only appear once on the calendar, while recurring events may show up any number of times depending on the recurrence pattern selected by a user. Either type of event may have a defined start and end time, or it may be an all-day event which lasts from midnight to 11:59 PM.
Furthermore, individual instances of a recurring event may be deleted or edited, creating a new event which takes its place. The event created by a deleted instance instructs the calendar not to render that day’s instance of the recurring event.
Calendar events can be distinguished by looking at the fRecurrence, fAllDayEvent, and EventType field values:
Type | Description | fRecurrence | fAllDayEvent | EventType |
Single event | An event created with the All Day Event and Recurrence checkboxes unselected. | False | False | 0 |
All-day event | An event created with the All Day Event checkbox selected. | False | True | 0 |
Recurring event | An event created with the Recurrence checkbox selected. Has a recurrence icon in the All Events view. Appears as a single master event on the All Events view, but as recurring instances on the Current Events and Calendar views. | True | False | 1 |
Recurring all-day event | Same as above, but with the All Day Event checkbox selected at creation time. | True | True | 1 |
Recurrence exception | Created by editing an instance of a recurring event. Has a strikethrough recurrence icon in the All Events view. | True | False | 4 |
All-day recurrence exception | Same as above, but created by editing an instance of an all-day recurring event. | True | True | 4 |
Deleted instance of a recurring event | Created by deleting a instance of a recurring event. Title is prefixed with “Deleted:” in the All Events view, and is hidden in the Current Events and Calendar views. | True | False | 3 |
Deleted instance of an all-day recurring event | Same as above, but created by deleting an instance of an all-day recurring event. | True | True | 3 |
Understanding recurring events
Recurring events have a number of subtleties not found in single events. Most importantly, one recurring event item expands into any number of recurring event instances when it is rendered in the calendar view. Recurring events also make use of fields left empty in single events.
Certain fields are interpreted differently depending on whether the item in question is a recurring event or a single event:
Field | Value for single event | Value for recurring event |
EventDate | Start date and time | Start date and time set for the recurring event when it was created, which may be an earlier date than the first instance of the recurring event. |
EndDate | End date and time | End date and time for the last instance of the recurring event. For recurring events with no end date, this is a computed date several years in the future. |
Duration | The time in seconds between EventDate and EndDate. | The duration in seconds of an individual instance of the recurring event. |
Similarly, exceptions and deleted instances have fields which relate the event back to the parent recurring event:
Field | Value for exception or deleted instance |
MasterSeriesItemID | The ID of the recurring event from which this exception or deleted instance was made. |
RecurrenceID | The date and time of the instance of the recurring event which this exception or deleted instance takes the place of. |
Once you’ve determined that you’re working with a recurring event, you’ll likely want to work with individual instances of the recurrence.
Recurrence patterns and expanding events
Recurring events store the pattern used to display instances of the recurring event as XML in the RecurrenceData field. While this data is best used in a read-only fashion, the following are examples of the patterns you may encounter:
Recurrence type | RecurrenceData field value |
Daily every 1 days, no end date | <recurrence><rule> <firstDayOfWeek>su</firstDayOfWeek> <repeat><daily dayFrequency="1" /></repeat> <repeatForever>FALSE</repeatForever> </rule></recurrence> |
Weekly every Monday, Tuesday, and Wednesday, end by 5/31/2007 | <recurrence><rule> <firstDayOfWeek>su</firstDayOfWeek> <repeat><weekly mo="TRUE" tu="TRUE" we="TRUE" weekFrequency="1" /></repeat> <windowEnd>2007-05-31T22:00:00Z</windowEnd> </rule></recurrence> |
Monthly the third Wednesday of every 2 months, no end date | <recurrence><rule> <firstDayOfWeek>su</firstDayOfWeek> <repeat><monthlyByDay we="TRUE" weekdayOfMonth="third" monthFrequency="2" /></repeat> <repeatForever>FALSE</repeatForever> </rule></recurrence> |
Yearly every May 18, end after 10 instances | <recurrence><rule> <firstDayOfWeek>su</firstDayOfWeek> <repeat><yearly yearFrequency="1" month="5" day="18" /></repeat> <repeatInstances>10</repeatInstances> </rule></recurrence> |
Thankfully, you don’t need to parse this XML yourself to get the actual instances of the recurring event. Instead, you can use the SharePoint object model to expand recurring events during a given month:
// Get the Events list
SPSite site = new SPSite("http://localhost");
SPWeb web = site.RootWeb;
SPList calendarList = web.Lists["Calendar"];
// Construct a query that expands recurring events
SPQuery query = new SPQuery();
query.ExpandRecurrence = true;
query.Query = "<Where><DateRangesOverlap><FieldRef Name=\"EventDate\" /><FieldRef Name=\"EndDate\" /><FieldRef Name=\"RecurrenceID\" /><Value Type=\"DateTime\"><Month /></Value></DateRangesOverlap></Where>";
// Look forward from the beginning of the current month
query.CalendarDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
// Returns all items (including recurrence instances) that
// would appear in the calendar view for the current month
SPListItemCollection calendarItems = calendarList.GetItems(query);
foreach (SPListItem item in calendarItems)
{
Console.WriteLine(item["Title"] + ": starts "
+ item["EventDate"].ToString() + " and ends "
+ item["EndDate"].ToString());
}
Note that after expansion, each instance of a recurring event has the same ID as the recurring event that produced it. In other words, a recurring event with ID 7 will appear as many recurring event instances in the code above, each with an ID of 7.
Putting it to work
It’s always easier to learn from an example than from dry technical descriptions, and to that end I’ve developed a SharePoint Solution which uses the functionality I’ve described above. When installed and activated, it adds an “Export Calendar to iCal Format” button to the Actions menu of every Calendar list in the site collection as pictured below.
To convert events to RFC 2445 iCalendar format, I loop through each event, translating recurrence patterns into iCalendar format and associating exceptions and deleted instances with the recurring event they came from. The majority of the code deals with interpreting the recurrence pattern; however, there is also a significant amount of code to translate SharePoint timezone information as well.
You can download the iCalendar Exporter solution and source code, which have been released as part of the Community Kit for SharePoint, right here. Launch Visual Studio, take a look at my source code, and have fun developing custom solutions with the SharePoint calendar! Please leave a comment if you have any questions or want to showcase an innovative solution that you've built.
Matt Swann
SDET, SharePoint