Recurrences

Recurrences specify a series of instances in time that follow a regular pattern. In the simplest case, for example "Daily," there is a fixed amount of time between occurrences. In other cases, for example "at 3pm every 3rd Friday of the month," the time between occurrences varies. The Recurrence class captures these patterns and offers a convenient way to work with them.

Date/time Units

Time spans may be expressed in many different units. The DateTimeUnit enumeration type specifies the available units:

Values of the DateTimeUnit enumeration.

Name

Description

None

No special intervals are included.

None

The time unit is not applicable.

Microsecond

The time unit is one micro-second.

Millisecond

The time unit is one millisecond.

Second

The time unit is one second.

Minute

The time unit is one minute.

Hour

The time unit is one hour.

Day

The time unit is one day.

Week

The time unit is one week.

Month

The time unit is one month.

Quarter

The time unit is three months.

Semester

The time unit is six months.

Year

The time unit is one year.

Some units have a constant value. Others are based on the length of a month, which varies. Any time span can be expressed as the sum of a constant value and a number of months.

Recurrence Patterns

A recurrence pattern consists of two parts: a time span between occurrences and an offset from a larger time span. A recurrence does not have a start or end date. Start and end dates are specified when a concrete sequence of instances is generated.

Recurrences are immutable, and the Recurrence class does not have any constructors. Instead, a combination of several built-in recurrences and methods that derive new recurrences from existing ones are used. The predefined occurrences are defined as read-only static fields of the Recurrence class:

Built-in recurrences.

Name

Description

Hourly

An occurrence every hour.

Daily

A daily recurrence at midnight.

Weekly

A recurrence every 7 days.

Monthly

A recurrence every month.

Quarterly

A recurrence every three months.

Yearly

A recurrence every 12 months.

A number of fields define weekly recurrences on a specific day of the week. These are Mondays, Tuesdays, and so on.

In addition, the Every method lets you define a recurrence of arbitrary length of arbitrary units. This method has two overloads. The first has two arguments: the first argument is the number of units and the second specifies the unit. The second overload has only one argument, the unit, and takes the number of units to be 1. The code below shows some examples of simple recurrences:

C#
var everyDay = Recurrence.Daily;
var every5Minutes = Recurrence.Every(5.0, DateTimeUnit.Minute);

Once you have a basic recurrence object, several instance methods may be called to create more complex recurrences. The At(Int32, Int32, Double) lets you specify a time of day in hours, minutes and seconds. (Minutes and seconds are optional.)

C#
var at915am = Recurrence.Daily.At(9, 15);
var wednesdaysAt430pm = Recurrence.Wednesdays.At(16, 30);

For monthly occurrences, the Day(Int32, Boolean) method returns a recurrence on the specified day of the month. An optional second argument specifies whether the day should be counted from the end. It is also possible to specify a recurrence on a specific day of the week each month. There is one method for each day of the week. Each method takes one or two arguments. The first is the week number, and the second is whether weeks are counted from the end.

C#
var eightOfTheMonth = Recurrence.Monthly.Day(8);
var lastDayOfTheMonth = Recurrence.Monthly.Day(1, fromEnd: true);
var thirdFriday = Recurrence.Monthly.Friday(3);
var lastThursday = Recurrence.Monthly.Thursday(1, fromEnd: true);

Creating indexes from recurrences

The CreateDateTimeIndex method constructs an Index<T> of DateTime values that contains the instances of the recurrence during a time period. This method has three overloads.

The first overload takes two or three arguments. The first argument is the start date of the time period. The second argument is the number of occurrences to include in the index. The optional third argument is a SortOrder value that specifies whether the dates should be listed in ascending or descending order. The default is ascending.

The second overload takes four arguments. The first is once again the start date. The second is the end date. The third and fourth arguments are Boolean values that specify whether the end points should be included in the index. The third overload is similar to the third. The only difference is that the second argument is a TimeSpan. The end date is calculated by adding this time span to the start date.

In the below example, we create an index containing the third Friday of each month in 2017. We use each of the overloads to create the same index three times:

C#
var thirdFriday = Recurrence.Monthly.Friday(3);
var startDate = new DateTime(2017, 1, 1);
var index1 = thirdFriday.CreateDateTimeIndex(startDate, 12);
var endDate = new DateTime(2018, 1, 1);
var index2 = thirdFriday.CreateDateTimeIndex(startDate, endDate, false, false);
var length = TimeSpan.FromDays(365);
var index3 = thirdFriday.CreateDateTimeIndex(startDate, length, false, false);