Search code examples
c#.neticalendarical-dotnet

Recurring events with daylight savings time support using ical.net


What's the best way to implement recurring dates which automatically adjust for DST? E.g an event should recur every day at 10am EST even when DST is in effect and should not jump backwards or forwards 1hr.

Currently I'm storing dates as UTC, but I also have access to the original timezone.


Solution

  • In the general case in date and time programming, it's not really possible to express rules about future times using UTC as your starting point if your target time zone isn't also UTC. Time zones are socio-political, and the rules change.

    To that end, the solution is to express your CalDateTimes with local time zones. Once you've done that, GetOccurrences() will compute the recurrence set correctly.

    var start = DateTime.Parse("2017-02-01 11:00");
    var end = start.AddHours(1);
    var dailyUntilSummer = new RecurrencePattern(FrequencyType.Daily, 1)
    {
        Until = DateTime.Parse("2017-07-01 12:00"),
    };
    var calendarEvent = new Event
    {
        Start = new CalDateTime(start, "America/New_York"),
        End = new CalDateTime(end, "America/New_York"),
        RecurrenceRules = new List<IRecurrencePattern> { dailyUntilSummer },
    };
    
    var calendar = new Calendar();
    calendar.Events.Add(calendarEvent);
    
    var occurrences = calendar.GetOccurrences(start, start.AddMonths(6))
        .Select(o => new {Local = o.Period.StartTime, Utc = o.Period.StartTime.AsUtc})
        .OrderBy(o => o.Local)
        .ToList();
    

    If you set a breakpoint after occurrences, and look at its contents, you'll see that on March 12, the UTC time goes from 16:00 to 15:00 while the local time remains stable. On March 12, America/New_York went from UTC-5 to UTC-4 when the clocks changed:

    (Behind the scenes, ical.net uses NodaTime to do the time zone conversions.)