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.

[topads][/topads]

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.

[signupform][/signupform]

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!


Consider giving back by getting me a coffee (or a couple) by clicking the following button:

[bottomads][/bottomads]

Spread the love

13 thoughts on “Create iCal ics Files in C# ASP.NET MVC – Several Methods

  1. rianjs says:

    StringBuilder.AppendLine will use platform-specific newline characters. So if you’re doing .NET Core on Linux, that will be \n. RFC-5545 specifies \r\n as the line break/wrap for icalendar text. So you should use StringBuilder.Append instead if you’re going to build it manually.

    The spec also specifies wrapping long lines at 75 octets, so if you’re not taking into account string length, you could be generating invalid ical text. Some clients care, some don’t. ical.net, for example, doesn’t care, but other applications will barf. (Actually ical.net doesn’t get line folding quite right, either, as it wraps at 75 *characters* which is OK for ASCII-range characters, but might do the wrong thing if the summary contained emoji.)

    Other reasons to use a library. 🙂

    • jgezau says:

      Hey thanks for the input, I wasn’t aware of the platform-specific newline character with AppendLine. ical.net is a great library indeed, I just prefer to use StringBuilder 🙂

  2. abc says:

    What is reg in your code?

    • jgezau says:

      A “Registration” object that contains a “Reservations” object list

      public class Registration
      {

      public List Reservations { get; set; }

      ]

      So basically looping over a list of reservations.

  3. mgrahl5354 says:

    Another thought on your “own implementation”: did you think about correct times and timezones?
    Its not easy to make sure the calendar client/software interprets your times always correctly.
    Please remember daylight saving times and time zones. The UTC is calculated when the ical is created. But if the event / alarm is in the future and maybe in between the daylight saving time ends or begins, the event / alarm would “move”…

  4. Steve Ralston says:

    I have a custom-made C# .NET app where we can enter and display calendar info. Currently this info is in a proprietary format. I also have a WordPress site using the EventCalendar plugin. I’d like to be able to create an iCal stream, or somehow, in real time, provide updates from the .NET site to the WordPress site. I know, using EventAggregator, EventCalendar can display data from an iCal feed. It sounds like what you’re talking about here could be a solution. Do you know of any caveats, or other things I should be aware of, as I dig deeper into this? Thanks for any input.

  5. Dattatray Arsule says:

    How to save event in organizer calendar automatically?

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.