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.
elmah.io: Error logging and Uptime Monitoring for your web apps
This blog post is brought to you by elmah.io. elmah.io is error logging, uptime monitoring, deployment tracking, and service heartbeats for your .NET and JavaScript applications. Stop relying on your users to notify you when something is wrong or dig through hundreds of megabytes of log files spread across servers. With elmah.io, we store all of your log messages, notify you through popular channels like email, Slack, and Microsoft Teams, and help you fix errors fast.
See how we can help you monitor your website for crashes Monitor your website