Skip to main content

Cloud

Generating Outlook calendar invitations on the fly using C# and .NET

Recently I had a need to build functionality of generating Outlook calendar events invitations on the fly.

Apparently there are two types of calendar invitation files that can be used for that purpose: .vcs and .ics. If a file with this extension is open, an event will be opened allowing the user to review, modify and save or cancel.

The file format for both of these is a very simple text file. I was not able to find official confirmation for that but it appears that .vcs is version 1.0 of the file specification while .ics is version 2.0. It also appears that .ics format is backward compatible with .vcs. Here is a sample of the .ics file corresponding to picture above:

BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
STATUS:TENTATIVE
DTSTART:20091001T050000Z
DTEND:20091003T040000Z
SUMMARY;ENCODING=QUOTED-PRINTABLE:My Event
LOCATION;ENCODING=QUOTED-PRINTABLE:Puerto Vallarta
DESCRIPTION;ENCODING=QUOTED-PRINTABLE:I am going Mexico.=0ANo work – just sun, ocean and rest…
END:VEVENT
END:VCALENDAR

As you can see the file format is very simple. For more information you can reference the article on Wiki: http://en.wikipedia.org/wiki/ICalendar, although it only gives you an overview rather than a complete specification. I have created a simple C# class that encapsulates the functionality of generating such files (.NET 3.5 is required, since I use class extension). Here is an example of a simple web page that generates .ics file on the fly using that class:

Default.aspx
<%
@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default" %>

Default.aspx.cs

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using VCSFileHandling;

namespace WebApplication1

{

public partial class _Default : System.Web.UI.Page

{

protected void Page_Load(object sender, EventArgs e)

{

CalendarFile calFile = new CalendarFile("My Event", DateTime.Parse("10/1/2009 0:00"), DateTime.Parse("10/2/2009 23:00"));

calFile.location = "Puerto Vallarta";

calFile.description = "I am going Mexico.nNo work – just sun, ocean and rest…";

calFile.status = CalendarFile.CFStatus.Tentative;

calFile.allDayEvent = false;

Response.AddHeader("content-disposition", "attachment; filename=PTO%20Request.ics");

Response.ContentType = "text/x-vCalendar";

Response.Write(calFile.ToString());

}

}

}
			

Below is the code for the classes encapsulating the ics-file generating functionality. The code consists of two classes: CalendarFile and CalndarFileExtensions. CalendarFile is the main class while CalndarFileExtesions is a helper class containing class extensions for DateTime and String that I use in main class.

Please feel free to use this code for your own purposes. If you want to write your own .ics/.vcs generator, keep in mind a few gotchas for this simple file format:

  1. Generally, this format is not Outlook specific but I used some Microsoft-specifc extensions to the file format in order to show BUSY and OUT OF THE OFFICE statuses. I was not able to find a way to encode these using the generic .ics file format.
  2. Start and end date are in universal time format, don’t forget to convert.
  3. If you want to book an "all day event" make sure that date start and date end do not include time.
  4. Multiline text is encoded using the "=0D" for r and "0A" for "n". If you need to encode any other special characters, the pattern is self-evident.

Other than that the code below is very simple and should be self explanatory. It would be easy now to create functionality where emails with .ics attachments are autogenerated. Alternatively the emails could contain a link pointing to a url auto generating .ics file.

CalendarFile.cs

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace VCSFileHandling

{

public class CalendarFile

{

#region Constructors

public CalendarFile(string p_title, DateTime p_startDate, DateTime p_endDate)

{

if (p_startDate > p_endDate) throw new Exception("VCSFile: Attept to initialize a calendar event with start date after end date");

_summary = p_title;

_startDate = p_startDate;

_endDate = p_endDate;

}

#endregion

#region Enums

public enum CFStatus { Free, Busy, Tentative, OutOfTheOffice };

#endregion

#region Public Methods

override public string ToString()

{

StringBuilder calEvent = new StringBuilder();

calEvent.AppendLine("BEGIN:VCALENDAR");

calEvent.AppendLine("VERSION:2.0");

calEvent.AppendLine("BEGIN:VEVENT");

switch (status)

{

case CFStatus.Busy:

calEvent.AppendLine("X-MICROSOFT-CDO-BUSYSTATUS:BUSY");

break;

case CFStatus.Free:

calEvent.AppendLine("TRANSP:TRANSPARENT");

break;

case CFStatus.Tentative:

calEvent.AppendLine("STATUS:TENTATIVE");

break;

case CFStatus.OutOfTheOffice:

calEvent.AppendLine("X-MICROSOFT-CDO-BUSYSTATUS:OOF");

break;

default:

throw new Exception("Invalid CFStatus");

}

if (allDayEvent)

{

calEvent.AppendLine("DTSTART;VALUE=DATE:" + startDate.ToCFDateOnlyString());

calEvent.AppendLine("DTEND;;VALUE=DATE:" + endDate.ToCFDateOnlyString());

}

else

{

calEvent.AppendLine("DTSTART:" + startDate.ToCFString());

calEvent.AppendLine("DTEND:" + endDate.ToCFString());

}

calEvent.AppendLine("SUMMARY;ENCODING=QUOTED-PRINTABLE:" + summary.ToOneLineString());

if (location != "") calEvent.AppendLine("LOCATION;ENCODING=QUOTED-PRINTABLE:" + location.ToOneLineString());

if (description != "") calEvent.AppendLine("DESCRIPTION;ENCODING=QUOTED-PRINTABLE:" + description.ToCFString());

calEvent.AppendLine("END:VEVENT");

calEvent.AppendLine("END:VCALENDAR");

return calEvent.ToString();

}

#endregion

#region Accessors and Mutators

public string summary

{

get { return _summary; }

set { _summary = value; }

}

public DateTime startDate

{

get { return _startDate; }

//set { _startDate = value; }

}

public DateTime endDate

{

get { return _endDate; }

//set { _endDate = value; }

}

public string location

{

get { return _location; }

set { _location = value; }

}

public string description

{

get { return _description; }

set { _description = value; }

}

public bool allDayEvent

{

get { return _bookFullDays; }

set { _bookFullDays = value; }

}

public CFStatus status

{

get { return _status; }

set { _status = value; }

}

#endregion

#region Internal Fields

//mandatory fields initialized in the constructor

private string _summary;

private DateTime _startDate;

private DateTime _endDate;

//fields with default values

//private ShowStatusAs _showStatusAs = ShowStatusAs.Busy;

private CFStatus _status = CFStatus.Busy;

private string _location = "";

private string _description = "";

private bool _bookFullDays = false;

#endregion

}

}

CalendarFileExtensions.cs

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace VCSFileHandling

{

public static class CalendarFileExtentions

{

public static string ToCFString(this DateTime val)

{

//format: YYYYMMDDThhmmssZ where YYYY = year, MM = month, DD = date, T = start of time character, hh = hours, mm = minutes,

//ss = seconds, Z = end of tag character. The entire tag uses Greenwich Mean Time (GMT)

return val.ToUniversalTime().ToString("yyyyMMddThhmmssZ");

}

public static string ToCFDateOnlyString(this DateTime val)

{

//format: YYYYMMDD where YYYY = year, MM = month, DD = date, T = start of time character, hh = hours, mm = minutes,

//ss = seconds, Z = end of tag character. The entire tag uses Greenwich Mean Time (GMT)

return val.ToUniversalTime().ToString("yyyyMMdd");

}

public static string ToCFString(this string str)

{

return str.Replace("r", "=0D").Replace("n", "=0A");

}

public static string ToOneLineString(this string str)

{

return str.Replace("r", " ").Replace("n", " ");

}

}

}

Please feel free to use the two classes above if you need to implement calndar files in .NET. The information presented in this blog was gathered from online resources and reverse engineering. Please let me know If you find it incorrect or incomplete.

Thoughts on “Generating Outlook calendar invitations on the fly using C# and .NET”

  1. I am working on a project, I am trying to create a calendar entry on the fly. However the trick is the hosting server is in one time zone and I am working in a different time zone. When I run the app it creates the file, however it is 2 hours off because of time zone differences, I have tried many different ways to fix it and having lots of trouble attempting to fix it.

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.