Outlook Style Recurrence Calculator

Recently I have been researching the ability to have Outlook style recurrence pattern calculations in .NET.  Since I could not find anything, I just wrote one myself.  It took a bit of time, but now the project is published on GitHub and NuGet.

Library written for .NET that perform Outlook style recurrence calculations. The key to usage is the IRecurrence interface that allows you to specify the recurrence pattern.

I have used for the most part the interface that Outlook supports. You can find the details on MSDN site:http://msdn.microsoft.com/en-us/library/microsoft.office.interop.outlook.recurrencepattern(v=office.15).aspx I made one change – I changed the DayOfWeekMask from flags based enumeration to just 7 flags – one for each day of the week. This will allow you to easily expose the same class at the user interface to allow the user to set flags for the days of the week. In order to support things like day, week day and weekend, you just set appropriate days flags. For example, set all the flags for the “day”, Sunday and Saturday for the “weekend”. I also decided not to support “no end date”, so date is required or number of occurrences.

It is very easy to use. Implement IRecurrence in any of your class, create an instance of the calculator, and call Calculate method. An exception will be thrown if your pattern is invalid. You can also call Validate manually. If you get an empty string, you are valid. Otherwise you will get the message telling you what is wrong with your pattern.

The project includes tests that will tell you how to use the calculator.

Here is what IRecurrence look like.

using System;

namespace RecurrenceCalculator
{
    /// <summary>
    /// Interface IRecurrence
    /// Recurrence setup information
    /// </summary>
    public interface IRecurrence
    {
        /// <summary>
        /// Gets the day of month.
        /// </summary>
        /// <value>The day of month.</value>
        int DayOfMonth { get; }

        /// <summary>
        /// Gets a value indicating whether this <see cref="IRecurrence"/> event can occur on Sunday.
        /// </summary>
        /// <value><c>true</c> if event can occur on Sunday; otherwise, <c>false</c>.</value>
        bool Sunday { get; }

        /// <summary>
        /// Gets a value indicating whether this <see cref="IRecurrence"/> event can occur on Monday.
        /// </summary>
        /// <value><c>true</c>  if event can occur on Monday; otherwise, <c>false</c>.</value>
        bool Monday { get; }

        /// <summary>
        /// Gets a value indicating whether this <see cref="IRecurrence"/> event can occur on Tuesday.
        /// </summary>
        /// <value><c>true</c>  if event can occur on Tuesday; otherwise, <c>false</c>.</value>
        bool Tuesday { get; }

        /// <summary>
        /// Gets a value indicating whether this <see cref="IRecurrence"/> event can occur on Wednesday.
        /// </summary>
        /// <value><c>true</c>  if event can occur on Wednesday; otherwise, <c>false</c>.</value>
        bool Wednesday { get; }

        /// <summary>
        /// Gets a value indicating whether this <see cref="IRecurrence"/> event can occur on Thursday.
        /// </summary>
        /// <value><c>true</c>  if event can occur on Thursday; otherwise, <c>false</c>.</value>
        bool Thursday { get; }

        /// <summary>
        /// Gets a value indicating whether this <see cref="IRecurrence"/> event can occur on Friday.
        /// </summary>
        /// <value><c>true</c>  if event can occur on Friday; otherwise, <c>false</c>.</value>
        bool Friday { get; }

        /// <summary>
        /// Gets a value indicating whether this <see cref="IRecurrence"/> event can occur on Saturday.
        /// </summary>
        /// <value><c>true</c>  if event can occur on Saturday; otherwise, <c>false</c>.</value>
        bool Saturday { get; }

        /// <summary>
        /// Gets the instance of the occurrence, such as 1st or 2nd.  Set to 5 for the last instance
        /// </summary>
        /// <value>The of the occurrence, such as 1st or 2nd.  Set to 5 for the last instance.</value>
        int Instance { get; }

        /// <summary>
        /// Gets the day of the month.
        /// </summary>
        /// <value>The day.</value>
        int Day { get; }

        /// <summary>
        /// Gets the interval, such as every 2 days or every 3 years.
        /// </summary>
        /// <value>The interval, such as every 2 days or every 3 years.</value>
        int Interval { get; }

        /// <summary>
        /// Gets the month of year for yearly occurrences.
        /// </summary>
        /// <value>The month of year.</value>
        int MonthOfYear { get; }

        /// <summary>
        /// Gets the total number of occurrences to generate
        /// </summary>
        /// <value>The total number of occurrences to generate.</value>
        int Occurrences { get; }

        /// <summary>
        /// Gets the start date, including time which is appointment type.
        /// </summary>
        /// <value>The start date, including time which is appointment type.</value>
        DateTime StartDate { get; }

        /// <summary>
        /// Gets the type of the recurrence.
        /// </summary>
        /// <value>The type of the recurrence.</value>
        RecurrenceType RecurrenceType { get; }

        /// <summary>
        /// Gets the end date.  Can be the last date of the event.  
        /// AAlternative to number of occurrences.
        /// </summary>
        /// <value>The end date.  Can be the last date of the event.  
        /// AAlternative to number of occurrences..</value>
        DateTime? EndDate { get; }

    }
}
private Calculator calendarUtility;
private readonly DateTime startDate = new DateTime(2014, 1, 31, 16, 0, 0);

[TestInitialize]
public void Setup()
{
    calendarUtility = new Calculator();
}

private AppointmentRecurrence CreateWeeklyRecurrence()
{
    return new AppointmentRecurrence
    {
        RecurrenceTypeId = (int)RecurrenceType.Weekly,
        Interval = 2,
        Sunday = false,
        Monday = false,
        Tuesday = true,
        Wednesday = false,
        Thursday = true,
        Friday = false,
        Saturday = false,
        StartDate = startDate,
        Occurrences = 5
    };
}

[TestMethod]
public void Should_Generate_Weekly_Recurrences_Without_End_Date()
{
    var recurrence = CreateWeeklyRecurrence();
    var occurrences = calendarUtility.CalculateOccurrences(recurrence).ToList();
    Assert.AreEqual(5, occurrences.Count, "Should create 5 occurrences");
    Assert.AreEqual(new DateTime(2014, 2, 11).Add(startDate.TimeOfDay), occurrences[0], "Date 1 should be correct");
    Assert.AreEqual(new DateTime(2014, 2, 13).Add(startDate.TimeOfDay), occurrences[1], "Date 2 should be correct");
    Assert.AreEqual(new DateTime(2014, 2, 25).Add(startDate.TimeOfDay), occurrences[2], "Date 3 should be correct");
    Assert.AreEqual(new DateTime(2014, 2, 27).Add(startDate.TimeOfDay), occurrences[3], "Date 4 should be correct");
    Assert.AreEqual(new DateTime(2014, 3, 11).Add(startDate.TimeOfDay), occurrences[4], "Date 5 should be correct");
}

Enjoy.

3 Comments

  1. Hey, Aidan. I did see it. All I needed was calculate dates so that I can save them, and I honestly could not find a way to do just that from the Api page. I did not need execution engine either.

Leave a Reply to Sergey Barskiy Cancel reply

Your email address will not be published. Required fields are marked *