Create iCal ics Files in C# ASP.NET MVC – Several Methods

Creating an iCal (ics file) is really simple. In this post I will show you how to create one using iCal.NET library and one using plain ol StringBuilder.






pCloud Premium

First of all, this is the basic format for an iCal

BEGIN:VCALENDAR
PRODID:-//LEEDS MUSIC SCENE//EN
VERSION:2.0
METHOD:PUBLISH
BEGIN:VEVENT
SUMMARY:BAND @ VENUE
PRIORITY:0
CATEGORIES:GIG
CLASS:PUBLIC
DTSTART:STARTTIME
DTEND:ENDTIME
URL:LINK TO LMS GIG PAGE
DESCRIPTION:FULL BAND LIST
LOCATION:VENUE
END:VEVENT
END:VCALENDAR

Source: This PasteBin

The calendar begins with the keyword BEGIN:VCALENDAR and ends with END:VCALENDAR. In between the calendar you can add as many events as you want beginning each event with BEGIN:VEVENT and ending with END:VEVENT.

There is a catch when adding multiple events to a calendar, and that is the type of METHOD you use. With METHOD:PUBLISH, you can add multiple events and upoin opening the iCal with MS Outlook it will create a new calendar, separate from your main Exchange calendar. METHOD:REQUEST creates a calendar invite, so you can only include one event and upon openning with MS Outlook it will ask you to accept the event and add it to your main Exchange calendar. With this method you have to include the orginizer (ORGANIZER;CN=Your Name:mailto:email@company.com)

Refer to Methods for VEVENT Calendar Components

Using iCal.NET

Download iCal.NET via nuget package then import the libraries to your class.

In the code below I create an email message and at the end I attach the iCal to the email message.

Everything else in the code below is pretty self-explanatory. You create a new calendar, loop thru a list of events, serialize the calendar, convert to bytes, put into a memory stream and finally attach it to the email message.

using Ical.Net;
using Ical.Net.DataTypes;
using Ical.Net.Serialization.iCalendar.Serializers;
using Ical.Net.Serialization;
...
MailMessage message = new MailMessage();
message.To.Add("johndoe@user.com");
message.From = new MailAddress("info@company.com", "Company, Inc");
message.Subject = "subject";
message.Body = "emailbody";
message.IsBodyHtml = true;
...
var calendar = new Ical.Net.Calendar();

foreach (var res in reg.Reservations) {
	calendar.Events.Add(new Event {
		Class = "PUBLIC",
		Summary = res.Summary,
		Created = new CalDateTime(DateTime.Now),
		Description = res.Details,
		Start = new CalDateTime(Convert.ToDateTime(res.BeginDate)),
		End = new CalDateTime(Convert.ToDateTime(res.EndDate)),
		Sequence = 0,
		Uid = Guid.NewGuid().ToString(),
		Location = res.Location,
	});
}

var serializer = new CalendarSerializer(new SerializationContext());
var serializedCalendar = serializer.SerializeToString(calendar);
var bytesCalendar = Encoding.UTF8.GetBytes(serializedCalendar);
MemoryStream ms = new MemoryStream(bytesCalendar);
System.Net.Mail.Attachment attachment = new System.Net.Mail.Attachment(ms, "event.ics", "text/calendar");

message.Attachments.Add(attachment);
...

The output of the serializedCalendar calendar is similar to my sample string calendar at the top of the page.

Using StringBuilder

I like this approach better as you have more flexibility and not be bound by a library that may or may not have all of the iCal attributes implemented. Now, don’t get me wrong iCal.NET is a great library, but I prefer to write the string myself.

Just as the above method, here I create a mail message, then with the StringBuilder I begin the calendar, loop thru my list of events creating calendar events, then ending the calendar and finally attaching my calendar to the email message. Pretty simple.

If you notice, I have DESCRIPTION attribute commented out, this only accepts plain text and X-ALT-DESC;FMTTYPE=text/html accepts HTML. Note that I have only tested on MS Outlook, not sure if X-ALT-DESC will work with other applications.

...
MailMessage message = new MailMessage();
message.To.Add("johndoe@user.com");
message.From = new MailAddress("info@company.com", "Company, Inc");
message.Subject = "subject";
message.Body = "emailbody";
message.IsBodyHtml = true;
...
StringBuilder sb = new StringBuilder();
string DateFormat = "yyyyMMddTHHmmssZ";
string now = DateTime.Now.ToUniversalTime().ToString(DateFormat);

sb.AppendLine("BEGIN:VCALENDAR");
sb.AppendLine("PRODID:-//Compnay Inc//Product Application//EN");
sb.AppendLine("VERSION:2.0");
sb.AppendLine("METHOD:PUBLISH");

foreach (var res in reg.Reservations) {
	DateTime dtStart = Convert.ToDateTime(res.BeginDate);
	DateTime dtEnd = Convert.ToDateTime(res.EndDate);

	sb.AppendLine("BEGIN:VEVENT");
	sb.AppendLine("DTSTART:" + dtStart.ToUniversalTime().ToString(DateFormat));
	sb.AppendLine("DTEND:" + dtEnd.ToUniversalTime().ToString(DateFormat));
	sb.AppendLine("DTSTAMP:" + now);
	sb.AppendLine("UID:" + Guid.NewGuid());
	sb.AppendLine("CREATED:" + now);
	sb.AppendLine("X-ALT-DESC;FMTTYPE=text/html:" + res.DetailsHTML);
	//sb.AppendLine("DESCRIPTION:" + res.Details);
	sb.AppendLine("LAST-MODIFIED:" + now);
	sb.AppendLine("LOCATION:" + res.Location);
	sb.AppendLine("SEQUENCE:0");
	sb.AppendLine("STATUS:CONFIRMED");
	sb.AppendLine("SUMMARY:" + res.Summary);
	sb.AppendLine("TRANSP:OPAQUE");
	sb.AppendLine("END:VEVENT");
}

sb.AppendLine("END:VCALENDAR");

var calendarBytes = Encoding.UTF8.GetBytes(sb.ToString());
MemoryStream ms = new MemoryStream(calendarBytes);
System.Net.Mail.Attachment attachment = new System.Net.Mail.Attachment(ms, "event.ics", "text/calendar");

message.Attachments.Add(attachment);
...

Bonus

If you are planning to create calendars from multiple classses, it would be beneficial to create a class or method to help you with this. Below is the current approach that I am taking.

One thing to note is that here, unlike in my above examples, I am using METHOD:REQUEST and creating multiple calendars invites for multiple events so that when the end user opens the calendar invite it will get added to their main Exchange calendar, also creating an alarm for each invite.

Create an iCalendar class that will hold the calendar properties.

iCalendar.cs

namespace Program
{
    public class iCalendar
    {
        public DateTime EventStartDateTime { get; set; }
        public DateTime EventEndDateTime { get; set; }
        public DateTime EventTimeStamp { get; set; }
        public DateTime EventCreatedDateTime { get; set; }
        public DateTime EventLastModifiedTimeStamp { get; set; }
        public string UID { get; set; }
        public string EventDescription { get; set; }
        public string EventLocation { get; set; }
        public string EventSummary { get; set; }
        public string AlarmTrigger { get; set; }
        public string AlarmRepeat { get; set; }
        public string AlarmDuration { get; set; }
        public string AlarmDescription { get; set; }

        public iCalendar() {
            EventTimeStamp = DateTime.Now;
            EventCreatedDateTime = EventTimeStamp;
            EventLastModifiedTimeStamp = EventTimeStamp;
        }
    }
}

Create a method or class

This class or method will give you a list of strings containing the calendar invites.

In my case I chose to create a public method in a separate class that holds “common tasks” shared accross my application, this method will receive a list of calendar objects (remember the iCalendar class I created above??) and return a list calendar strings.

public static List<string> iCals(List<iCalendar> iCals) {
	List<string> calendars = new List<string>();

	foreach(iCalendar iCal in iCals) {
		StringBuilder sb = new StringBuilder();

		//Calendar
		sb.AppendLine("BEGIN:VCALENDAR");
		sb.AppendLine("PRODID:-//Compnay Inc//Product Application//EN");
		sb.AppendLine("VERSION:2.0");
		sb.AppendLine("METHOD:REQUEST");

		//Event
		sb.AppendLine("BEGIN:VEVENT");
		sb.AppendLine("DTSTART:" + toUniversalTime(iCal.EventStartDateTime));
		sb.AppendLine("DTEND:" + toUniversalTime(iCal.EventEndDateTime));
		sb.AppendLine("DTSTAMP:" + toUniversalTime(iCal.EventTimeStamp));
		sb.AppendLine("ORGANIZER;CN=John Doe:mailto:johndoe@company.com");
		sb.AppendLine("UID:" + iCal.UID);
		//sb.AppendLine("ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=marydoe@company.com;X-NUM-GUESTS=0:mailto:marydoe@company.com");
		sb.AppendLine("CREATED:" + toUniversalTime(iCal.EventCreatedDateTime));
		sb.AppendLine("X-ALT-DESC;FMTTYPE=text/html:" + iCal.EventDescription);
		sb.AppendLine("LAST-MODIFIED:" + toUniversalTime(iCal.EventLastModifiedTimeStamp));
		sb.AppendLine("LOCATION:" + iCal.EventLocation);
		sb.AppendLine("SEQUENCE:0");
		sb.AppendLine("STATUS:CONFIRMED");
		sb.AppendLine("SUMMARY:" + iCal.EventSummary);
		sb.AppendLine("TRANSP:OPAQUE");

		//Alarm
		sb.AppendLine("BEGIN:VALARM");
		sb.AppendLine("TRIGGER:" + String.Format("-PT{0}M", iCal.AlarmTrigger));
		sb.AppendLine("REPEAT:" + iCal.AlarmRepeat);
		sb.AppendLine("DURATION:" + String.Format("PT{0}M", iCal.AlarmDuration));
		sb.AppendLine("ACTION:DISPLAY");
		sb.AppendLine("DESCRIPTION:" + iCal.AlarmDescription);
		sb.AppendLine("END:VALARM");

		sb.AppendLine("END:VEVENT");
		sb.AppendLine("END:VCALENDAR");

		calendars.Add(sb.ToString());
	}

	return calendars;
}

public static string toUniversalTime(DateTime dt) {
	string DateFormat = "yyyyMMddTHHmmssZ";

	return dt.ToUniversalTime().ToString(DateFormat);
}

Finally call everything and create the calendars

Loop thru the list of events creating a separate calendar invite and attaching them to the email message.

...
MailMessage message = new MailMessage();
message.To.Add("johndoe@user.com");
message.From = new MailAddress("info@company.com", "Company, Inc");
message.Subject = "subject";
message.Body = "emailbody";
message.IsBodyHtml = true;
...
List<iCalendar> iCals = new List<iCalendar>();

foreach (var res in reg.Reservations) {
	iCals.Add(new iCalendar {
		EventStartDateTime = Convert.ToDateTime(res.BeginDate),
		EventEndDateTime = Convert.ToDateTime(res.EndDate),
		UID = Guid.NewGuid().ToString(),
		EventDescription = res.DetailsHTML, 
		EventLocation = res.Location,
		EventSummary = res.Summary,
		AlarmTrigger = "30",
		AlarmRepeat = "2",
		AlarmDuration = "15",
		AlarmDescription = res.Details
	});
}

List<string> calendars = Common.iCals(iCals);

int i = 1;
foreach (string iCal in calendars) {
	var calendarBytes = Encoding.UTF8.GetBytes(iCal);
	MemoryStream ms = new MemoryStream(calendarBytes);

	System.Net.Mail.Attachment attachment = new System.Net.Mail.Attachment(ms, String.Format("invite{0}.ics", i), "text/calendar");
	message.Attachments.Add(attachment);

	i++;
}
...

Downloading the iCal

If you are not attaching the iCal to an email, instead you are making it available as a download, below is the partial code

public ActionResult DownloadiCal(...) 
{
	//Create the calendar
	...
	
	byte[] calendarBytes = System.Text.Encoding.UTF8.GetBytes(iCal);	//iCal is the calendar string
	
	return File(calendarBytes, "text/calendar", "event.ics");
}

Share if you liked this little tutorial!





Esau Silva
Software Engineer at Region One ESC
Microsoft Full Stack Application Developer
If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed.
Share