Skip to content

Working with recurring elements

Rian Stockbower edited this page Nov 24, 2017 · 7 revisions

It's impossible to provide an example of every type of recurrence scenario, so here are a few that represent some common use cases. If you'd like to see a specific example laid out, please create an issue.

How to think about occurrences

If you create an event or an alarm that happens more than once, it typically has a start time, an end time, and some rules about how and when it repeats:

  • "Daily, forever"
  • "Every other Tuesday until the end of the year"
  • "The fourth Thursday of every November"
  • "Every day for all of time, except Sundays"

You then want to search for occurrences of that event during a given time period. This kicks off the machinery which generates the set of occurrences of that event that match your search criteria.

Exceptions to recurrence rules

One of the bullets above says "Every day for all of time, except Sundays". Defining exceptions to recurrence rules is identical to defining rules themselves. Each exception is itself a recurrence. If you create the set of all possible recurrences, and then subtract the set of exceptions, you are left with the occurrences that match your search criteria.

Example below.

Daily, for the month of July

Suppose we want to create an event for July 1, between 07:00 and 08:00 that recurs every day through the end of July:

// The first instance of an event taking place on July 1, 2016 between 07:00 and 08:00.
var vEvent = new CalendarEvent
{
    Start = new CalDateTime(DateTime.Parse("2016-07-01T07:00")),
    End = new CalDateTime(DateTime.Parse("2016-07-01T08:00")),
};

// Recur daily through the end of the day, July 31, 2016
var recurrenceRule = new RecurrencePattern(FrequencyType.Daily, interval: 1)
{
    Until = DateTime.Parse("2016-07-31T11:59:59")
};

vEvent.RecurrenceRules = new List<RecurrencePattern> {recurrenceRule};
var calendar = new Calendar();
calendar.Events.Add(vEvent);

Now let's get the set of the occurrences of this event that take place between July 20 and Aug 5. If we count up the days, using our fingers and toes, there should be 12.

// Count the occurrences between July 20, and Aug 5
var searchStart = DateTime.Parse("2016-07-20");
var searchEnd = DateTime.Parse("2016-08-05");
var occurrences = calendar.GetOccurrences(searchStart, searchEnd);
Assert.AreEqual(12, occurrences.Count);

Every other Tuesday until the end of the year

Suppose we have an event taking place between 07:00 and 08:00 on Tuesday, July 5, and we want it to occur every other Tuesday between July 5, and the end of the year. We want to find the occurrences of this event between January 1, 2010(!) and Dec 31.

// An event taking place between 07:00 and 08:00, beginning July 5 (a Tuesday)
var vEvent = new CalendarEvent
{
    Start = new CalDateTime(DateTime.Parse("2016-07-05T07:00")),
    End = new CalDateTime(DateTime.Parse("2016-07-05T08:00")),
};

// Recurring every other Tuesday until Dec 31
var rrule = new RecurrencePattern(FrequencyType.Weekly, 2)
{
    Until = DateTime.Parse("2016-12-31T11:59:59")
};
vEvent.RecurrenceRules = new List<RecurrencePattern> { rrule };

// Count every other Tuesday between Jan 1, 2010 and Dec 31. There should be 13 in total
var searchStart = DateTime.Parse("2010-01-01");
var searchEnd = DateTime.Parse("2016-12-31");
var tuesdays = vEvent.GetOccurrences(searchStart, searchEnd);

Assert.AreEqual(13, tuesdays.Count);

The second day of every month

var rrules = new RecurrencePattern(FrequencyType.Monthly)
{
    ByMonthDay = new List<int> { 2 },  // Your day of the month goes here
};
var monthlyOnDay = new CalendarEvent
{
    Start = new CalDateTime(_now),
    End = new CalDateTime(_now.AddHours(1)),
    RecurrenceRules = new List<RecurrencePattern> { rrules },
};

The fourth Thursday of every November

In the United States, Thanksgiving is the fourth Thursday of every November. Let's count the number of Thanksgivings between Jan 1, 2000 and Jan 1, 2017. We should end up with 17.

// US Thanksgiving was on November 23 in 2000
var vEvent = new CalendarEvent
{
    Start = new CalDateTime(DateTime.Parse("2000-11-23T07:00")),
    End = new CalDateTime(DateTime.Parse("2000-11-23T19:00")),
};

// Thanksgiving happens yearly, with no end
var rrule = new RecurrencePattern(FrequencyType.Yearly, 1)
{
    Frequency = FrequencyType.Yearly,
    Interval = 1,
    ByMonth = new List<int> { 11 },
    ByDay = new List<IWeekDay> { new WeekDay { DayOfWeek = DayOfWeek.Thursday, Offset = 4 } },
    Until = DateTime.MaxValue
};
vEvent.RecurrenceRules = new List<RecurrencePattern> { rrule };

var searchStart = DateTime.Parse("2000-01-01");
var searchEnd = DateTime.Parse("2017-01-01");
var usThanksgivings = vEvent.GetOccurrences(searchStart, searchEnd);

Assert.AreEqual(17, usThanksgivings.Count);
foreach (var thanksgiving in usThanksgivings)
{
    Assert.IsTrue(thanksgiving.Period.StartTime.DayOfWeek == DayOfWeek.Thursday);
}

Every day in 2016, except Sundays

Exceptions are another set of rules. Instead of defining the set of things we should include, they define the set of things we should exclude.

Let's create an event that happens every day in 2016, between 07:00 and 08:00, except for Sundays:

var vEvent = new CalendarEvent
{
    Start = new CalDateTime(DateTime.Parse("2016-01-01T07:00")),
    End = new CalDateTime(DateTime.Parse("2016-12-31T08:00")),
    RecurrenceRules = new List<RecurrencePattern> { new RecurrencePattern(FrequencyType.Daily, 1)},
};

//Define the exceptions: Sunday
var exceptionRule = new RecurrencePattern(FrequencyType.Weekly, 1)
{
    ByDay = new List<IWeekDay> { new WeekDay(DayOfWeek.Sunday) }
};
vEvent.ExceptionRules = new List<RecurrencePattern> {exceptionRule};

var calendar = new Calendar();
calendar.Events.Add(vEvent);

// There should be 314 days that have event occurrences...
var searchStart = DateTime.Parse("2015-12-31");
var searchEnd = DateTime.Parse("2017-01-01");
var occurrences = calendar.GetOccurrences(searchStart, searchEnd);
Assert.AreEqual(314, occurrences.Count);