Generate iCal calendar with .NET using iCAL.NET 📅

Calendars are a great part of many people's every-day-life. Integrations with calendars can be a great way to interact very directly with your users and is often a very sought after feature if you have some kind of events as part of your platform. In this article, we will show how you can serve an endpoint in the iCal format for .NET with ease using the package iCal.NET.

Installing

The newest versions of the iCal.NET package is not supported in the newest versions of .NET Core. So if your project is targeting .NET Core 3.0 or above then you will need to install version 4.1.2 of the package. For .NET Core 2.2 and below and .NET Framework you can just target the newest version. You can install the package using the package manager or by typing the following in the terminal.

dotnet add package Ical.Net

Creating an event and serializing

Now we can begin to construct calendars and serialize these and return them through an API endpoint. In practice, you would probably use data from either your appsettings, an external API, or a database, but in the following, we just hard code the different fields for the calendar. We do this in an action that could be in any of your controllers depending on your need.

public IActionResult Calendar()
{
    var calendar = new Calendar();

    var icalEvent = new CalendarEvent
    {
        Summary = "Title of event",
        Description = "Description for event",
        // 15th of march 2021 12 o'clock.
        Start = new CalDateTime(2021, 3, 15, 12, 0, 0),
        // Ends 3 hours later.
        End = new CalDateTime(2021, 3, 15, 15, 0, 0)
    };

    calendar.Events.Add(icalEvent);

    var iCalSerializer = new CalendarSerializer();
    string result = iCalSerializer.SerializeToString(calendar);

    return File(Encoding.ASCII.GetBytes(result), "text/calendar", "calendar.ics");
}

We first make the iCal.NET Calendar object which is what we will eventually serialize. Then we create a new event which we will add to the calendar. There are a lot of different fields for an event, but we have used just a handful of them in this example. The Start and End fields take a CalDateTime object which has many of the same constructor patterns as a normal Datetime object. You can also pass an existing Datetime object to the constructor of the CalDateTime if you already have that from some data source. In the end, we serialize the calendar into a string. We return this as a file by getting the byte encoding of the string and specifying the content-type and file name. The final result is the following:

BEGIN:VCALENDAR
PRODID:-//github.com/rianjs/ical.net//NONSGML ical.net 4.0//EN
VERSION:2.0
BEGIN:VEVENT
DESCRIPTION:Description for event
DTEND:20210315T150000
DTSTAMP:20210125T000000Z
DTSTART:20210315T120000
SEQUENCE:0
SUMMARY:Title of event
UID:0a533499-d13b-479f-844a-a5103df63254
END:VEVENT
END:VCALENDAR

Time zones

Sometimes local time is used in some systems. If no time zone is specified most systems will assume time zone: UTC 0. This can be fixed by adding a time zone to the calendar like so:

var calendar = new Calendar();
calendar.AddTimeZone(new VTimeZone("Europe/Copenhagen"));

iCal.NET uses the library NodaTime to handle the time zones and time calculations. So you can use any of the identifiers they use to specify the time zone e.g. "America/Barbados" or "Etc/UTC" as it can be seen on the following list: https://nodatime.org/TimeZones. We get the following result from this addition.

BEGIN:VCALENDAR
PRODID:-//github.com/rianjs/ical.net//NONSGML ical.net 4.0//EN
VERSION:2.0
BEGIN:VTIMEZONE
TZID:Europe/Copenhagen
X-LIC-LOCATION:Europe/Copenhagen
END:VTIMEZONE
BEGIN:VEVENT
DESCRIPTION:Description for event
DTEND:20210315T150000
DTSTAMP:20210125T000000Z
DTSTART:20210315T120000
SEQUENCE:0
SUMMARY:Title of event
UID:c08a1d09-d76c-4c55-b318-ff064e26b0d4
END:VEVENT
END:VCALENDAR

Reoccurring events

Another common action is to have events that happen regularly for example every week or day. We can express pretty complex rules using a RecurrencePattern. An example of such a reoccurring event could be the Blazor Developments Sprints on the second Thursday of every month. Let's try to model this.

var recurrence = new RecurrencePattern
{
    Frequency = FrequencyType.Monthly,  // Happens monthly
    Interval = 1,                       // Every month. Not skipping any
    ByDay = new List<WeekDay> {new WeekDay {
        DayOfWeek = DayOfWeek.Thursday, // On a Thursday
        Offset = 2,                     // On the second Thursday
    } },
    Until = new DateTime(2022, 1, 13)   // Continue this rule until 2022
};

var icalEvent = new CalendarEvent
{
    Summary = "Blazor Development Sprints",
    Start = new CalDateTime(2021, 1, 14),
    IsAllDay = true,
    RecurrenceRules = new List<RecurrencePattern>() { recurrence }
};

We also set the event to be a whole-day event. This results in the following output.

BEGIN:VCALENDAR
PRODID:-//github.com/rianjs/ical.net//NONSGML ical.net 4.0//EN
VERSION:2.0
BEGIN:VEVENT
DTSTAMP:20210125T000000Z
DTSTART;VALUE=DATE:20210114
RRULE:FREQ=MONTHLY;UNTIL=20220113T000000;BYDAY=2TH
SEQUENCE:0
SUMMARY:Blazor Development Sprints
UID:03cf50de-ba2f-433e-97b6-6aa2881ad69c
END:VEVENT
END:VCALENDAR

Conclusion

In this article, we have seen how to add events to an iCal.NET calendar and then serialize it and serve it through an action. We have also touched upon how we add time zones to the calendar. In the end, we saw how we can define reoccurring events through an example. If you have any feedback for this article or questions on the details then feel free to reach out.