Recurrence patterns and EWS
Learn about recurrence patterns and recurring series in Exchange.
A recurring series is an appointment or meeting that repeats according to a defined pattern. A recurring series can either have a specific number of occurrences or can repeat indefinitely. Additionally, a recurring series can have exceptions that don't follow the pattern of the rest of the occurrences, and can have occurrences that have been deleted from the pattern. You can use the EWS Managed API and EWS to work with recurring series and their associated calendar items.
Recurring calendar items
All calendar items fall into one of the following four categories:
Non-recurring calendar items
Recurring masters
Occurrences in a series
Modified occurrences in a series, known as exceptions
In this article, we'll look at the three types of calendar items that are part of a recurring series.
It's helpful to understand how recurring series are implemented on the Exchange server. Instead of creating a separate distinct item for each occurrence in a recurring series, the server creates only one actual item in the calendar, known as the recurring master. The format of a recurring master is very similar to a non-recurring appointment, with the addition of recurrence pattern information. The server then generates occurrences based on the recurrence pattern in response to client requests for appointment information, using a process called expansion. These generated occurrences are not permanently stored on the server. This is important to understand because the way that you search for calendar items determines what information you receive and whether expansion occurs.
Recurrence patterns
The key piece to a recurring series that makes expansion possible is the recurrence pattern. The recurrence pattern is found on the recurring master, and describes a set of criteria for calculating occurrences based on the date and time of the recurring master.
Table 1. Available recurrence patterns
EWS Managed API class | EWS element | Examples |
---|---|---|
Recurrence.DailyPattern |
DailyRecurrence |
Repeat every day. Repeat every other day. |
Recurrence.MonthlyPattern |
AbsoluteMonthlyRecurrence |
Repeat every month on the tenth day of the month. Repeat every other month on the twenty-first day of the month. |
Recurrence.RelativeMonthlyPattern |
RelativeMonthlyRecurrence |
Repeat on the second Tuesday of every month. Repeat on the third Thursday of the month every three months. |
Recurrence.RelativeYearlyPattern |
RelativeYearlyRecurrence |
Repeat on the first Monday of August every year. |
Recurrence.WeeklyPattern |
WeeklyRecurrence |
Repeat every Monday. Repeat every Tuesday and Thursday every other week. |
Recurrence.YearlyPattern |
AbsoluteYearlyRecurrence |
Repeat on September 1st every year. |
The other important piece of information for a recurrence pattern is when the recurrence ends. This can be expressed as either a set number of occurrences, as an end date, or as having no end.
Table 2. Options for the end of a recurring series
EWS Managed API method/property | EWS element | Description |
---|---|---|
Recurrence.NumberOfOccurrences |
NumberedRecurrence |
The value of this property or element specifies the number of occurrences. |
Recurrence.EndDate |
EndDateRecurrence |
The last occurrence in the series falls on or before the date specified by this property or element. |
Recurrence.HasEnd Recurrence.NeverEnds |
NoEndRecurrence |
The series has no end. |
Expanded vs. non-expanded views
Using the FindAppointments method in the EWS Managed API (or the FindItem operation with a CalendarView element in EWS) invokes the expansion process. This hides recurring master appointments from the result set, and instead presents an expanded view of that recurring series. Occurrences of and exceptions to the recurring master that fall within the parameters of the calendar view are included in the result set. Conversely, using the FindItems method in the EWS Managed API (or the FindItem operation with a IndexedPageItemView or FractionalPageItemView element in EWS), does not invoke the expansion process, and occurrences and exceptions are not included. Let's look at an example comparing the two methods.
Table 3. Methods and operations for finding appointments
EWS Managed API method | EWS operation | Expands series? | Items included in results |
---|---|---|---|
ExchangeService.FindAppointments |
FindItem operation with a CalendarView element |
Yes |
Non-recurring appointments, single occurrences of recurring series, and exceptions to recurring series |
ExchangeService.FindItems |
FindItem operation with an IndexedPageItemView element or FractionalPageItemView element |
No |
Non-recurring appointments and recurring master appointments |
Sadie has just signed her son up for swim team. The team has practice every Wednesday morning at 8:30 AM, starting July 2, with the last practice being on August 6. Not wanting to forget about practice, Sadie adds a recurring appointment to her calendar to remind her.
Table 4. Sadie's recurring appointment
Appointment field | Value |
---|---|
Subject |
Swim Team Practice |
Start |
July 2, 2014 8:30 AM |
End |
July 2, 2014 10:00 AM |
Recurs |
Every Wednesday |
Last occurrence |
August 6, 2014 8:30 AM |
A quick look at a calendar shows that the team will have a total of six practices. However, there aren't six distinct appointment items in the calendar. Instead, there is just one recurring master appointment representing the series.
Now let's look at finding appointments on Sadie's calendar that occur within the month of July. The following code example uses the FindItems method in the Exchange Managed API to produce a non-expanded view of Sadie's calendar.
PropertySet propSet = new PropertySet(AppointmentSchema.Subject,
AppointmentSchema.Location,
AppointmentSchema.Start,
AppointmentSchema.End,
AppointmentSchema.AppointmentType);
#region FindItems + ItemView method
ItemView itemView = new ItemView(100);
itemView.PropertySet = propSet;
List<SearchFilter> filterList = new List<SearchFilter>();
// Find appointments that start after midnight on July 1, 2014.
SearchFilter.IsGreaterThan startFilter = new SearchFilter.IsGreaterThan(AppointmentSchema.Start,
new DateTime(2014, 7, 1));
// Find appointments that end before midnight on July 31, 2014
SearchFilter.IsLessThan endFilter = new SearchFilter.IsLessThan(AppointmentSchema.End,
new DateTime(2014, 7, 31));
filterList.Add(startFilter);
filterList.Add(endFilter);
SearchFilter.SearchFilterCollection calendarFilter = new SearchFilter.SearchFilterCollection(LogicalOperator.And, filterList);
// This results in a call to EWS.
FindItemsResults<Item> results = service.FindItems(WellKnownFolderName.Calendar, calendarFilter, itemView);
foreach(Item appt in results.Items)
{
Console.WriteLine(appt.Subject);
}
That code results in the following FindItem operation request with an IndexedPageItemView element.
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<t:RequestServerVersion Version="Exchange2007_SP1" />
<t:TimeZoneContext>
<t:TimeZoneDefinition Id="Pacific Standard Time" />
</t:TimeZoneContext>
</soap:Header>
<soap:Body>
<m:FindItem Traversal="Shallow">
<m:ItemShape>
<t:BaseShape>IdOnly</t:BaseShape>
<t:AdditionalProperties>
<t:FieldURI FieldURI="item:Subject" />
<t:FieldURI FieldURI="calendar:Location" />
<t:FieldURI FieldURI="calendar:Start" />
<t:FieldURI FieldURI="calendar:End" />
<t:FieldURI FieldURI="calendar:CalendarItemType" />
</t:AdditionalProperties>
</m:ItemShape>
<m:IndexedPageItemView MaxEntriesReturned="100" Offset="0" BasePoint="Beginning" />
<m:Restriction>
<t:And>
<t:IsGreaterThan>
<t:FieldURI FieldURI="calendar:Start" />
<t:FieldURIOrConstant>
<t:Constant Value="2014-07-01T07:00:00.000Z" />
</t:FieldURIOrConstant>
</t:IsGreaterThan>
<t:IsLessThan>
<t:FieldURI FieldURI="calendar:End" />
<t:FieldURIOrConstant>
<t:Constant Value="2014-07-31T07:00:00.000Z" />
</t:FieldURIOrConstant>
</t:IsLessThan>
</t:And>
</m:Restriction>
<m:ParentFolderIds>
<t:DistinguishedFolderId Id="calendar" />
</m:ParentFolderIds>
</m:FindItem>
</soap:Body>
</soap:Envelope>
The server's response includes only a single item, the recurring master, indicated by the CalendarItemType element value of RecurringMaster. The value of the ItemId element has been shortened for readability.
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="https://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<h:ServerVersionInfo MajorVersion="15" MinorVersion="0" MajorBuildNumber="939" MinorBuildNumber="16" Version="V2_11"
xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<m:FindItemResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<m:ResponseMessages>
<m:FindItemResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:RootFolder IndexedPagingOffset="1" TotalItemsInView="1" IncludesLastItemInRange="true">
<t:Items>
<t:CalendarItem>
<t:ItemId Id="AAMkADA5..." ChangeKey="DwAAABYA..." />
<t:Subject>Swim Team Practice</t:Subject>
<t:Start>2014-07-02T15:30:00Z</t:Start>
<t:End>2014-07-02T17:00:00Z</t:End>
<t:Location>Neighborhood Swimming Pool</t:Location>
<t:CalendarItemType>RecurringMaster</t:CalendarItemType>
</t:CalendarItem>
</t:Items>
</m:RootFolder>
</m:FindItemResponseMessage>
</m:ResponseMessages>
</m:FindItemResponse>
</s:Body>
</s:Envelope>
Now let's compare with an expanded view. The following code example uses the FindAppointments method in the EWS Managed API to create an expanded view of Sadie's calendar.
PropertySet propSet = new PropertySet(AppointmentSchema.Subject,
AppointmentSchema.Location,
AppointmentSchema.Start,
AppointmentSchema.End,
AppointmentSchema.AppointmentType);
CalendarView calView = new CalendarView(new DateTime(2014, 7, 1),
new DateTime(2014, 7, 31));
calView.PropertySet = propSet;
FindItemsResults<Appointment> results = service.FindAppointments(WellKnownFolderName.Calendar, calView);
foreach(Appointment appt in results.Items)
{
Console.WriteLine(appt.Subject);
}
This code results in the following FindItem operation request with a CalendarView element.
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<t:RequestServerVersion Version="Exchange2007_SP1" />
<t:TimeZoneContext>
<t:TimeZoneDefinition Id="Pacific Standard Time" />
</t:TimeZoneContext>
</soap:Header>
<soap:Body>
<m:FindItem Traversal="Shallow">
<m:ItemShape>
<t:BaseShape>IdOnly</t:BaseShape>
<t:AdditionalProperties>
<t:FieldURI FieldURI="item:Subject" />
<t:FieldURI FieldURI="calendar:Location" />
<t:FieldURI FieldURI="calendar:Start" />
<t:FieldURI FieldURI="calendar:End" />
<t:FieldURI FieldURI="calendar:CalendarItemType" />
</t:AdditionalProperties>
</m:ItemShape>
<m:CalendarView StartDate="2014-07-01T07:00:00.000Z" EndDate="2014-07-31T07:00:00.000Z" />
<m:ParentFolderIds>
<t:DistinguishedFolderId Id="calendar" />
</m:ParentFolderIds>
</m:FindItem>
</soap:Body>
</soap:Envelope>
This time, the server response includes five occurrences, one for each Wednesday in July. The CalendarItemType elements on these items all have a value of Occurrence. Note that the recurring master is not present in the response. The values of the ItemId elements have been shortened for readability.
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="https://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<h:ServerVersionInfo MajorVersion="15" MinorVersion="0" MajorBuildNumber="939" MinorBuildNumber="16" Version="V2_11"
xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<m:FindItemResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<m:ResponseMessages>
<m:FindItemResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:RootFolder TotalItemsInView="5" IncludesLastItemInRange="true">
<t:Items>
<t:CalendarItem>
<t:ItemId Id="AAMkADA6..." ChangeKey="DwAAABYA..." />
<t:Subject>Swim Team Practice</t:Subject>
<t:Start>2014-07-02T15:30:00Z</t:Start>
<t:End>2014-07-02T17:00:00Z</t:End>
<t:Location>Neighborhood Swimming Pool</t:Location>
<t:CalendarItemType>Occurrence</t:CalendarItemType>
</t:CalendarItem>
<t:CalendarItem>
<t:ItemId Id="AAMkADA7..." ChangeKey="DwAAABYA..." />
<t:Subject>Swim Team Practice</t:Subject>
<t:Start>2014-07-09T15:30:00Z</t:Start>
<t:End>2014-07-09T17:00:00Z</t:End>
<t:Location>Neighborhood Swimming Pool</t:Location>
<t:CalendarItemType>Occurrence</t:CalendarItemType>
</t:CalendarItem>
<t:CalendarItem>
<t:ItemId Id="AAMkADA8..." ChangeKey="DwAAABYA..." />
<t:Subject>Swim Team Practice</t:Subject>
<t:Start>2014-07-16T15:30:00Z</t:Start>
<t:End>2014-07-16T17:00:00Z</t:End>
<t:Location>Neighborhood Swimming Pool</t:Location>
<t:CalendarItemType>Occurrence</t:CalendarItemType>
</t:CalendarItem>
<t:CalendarItem>
<t:ItemId Id="AAMkADA9..." ChangeKey="DwAAABYA..." />
<t:Subject>Swim Team Practice</t:Subject>
<t:Start>2014-07-23T15:30:00Z</t:Start>
<t:End>2014-07-23T17:00:00Z</t:End>
<t:Location>Neighborhood Swimming Pool</t:Location>
<t:CalendarItemType>Occurrence</t:CalendarItemType>
</t:CalendarItem>
<t:CalendarItem>
<t:ItemId Id="AAMkADAA..." ChangeKey="DwAAABYA..." />
<t:Subject>Swim Team Practice</t:Subject>
<t:Start>2014-07-30T15:30:00Z</t:Start>
<t:End>2014-07-30T17:00:00Z</t:End>
<t:Location>Neighborhood Swimming Pool</t:Location>
<t:CalendarItemType>Occurrence</t:CalendarItemType>
</t:CalendarItem>
</t:Items>
</m:RootFolder>
</m:FindItemResponseMessage>
</m:ResponseMessages>
</m:FindItemResponse>
</s:Body>
</s:Envelope>
After you have a recurring master, an occurrence, or an exception, you can always retrieve the other related items. Given an occurrence or exception, you can retrieve the recurring master, and vice versa.
Working with recurring calendar items
You use all the same methods and operations to work with recurring series as you use to work with non-recurring calendar items. The difference is that, depending on the item you use to invoke those methods or operations, the actions you take can apply to the entire series, or just a single occurrence. Actions taken on the recurring master will apply to all occurrences in the series, while actions taken to a single occurrence or exception will only apply to that occurrence or exception.