Generating an iCalendar .ics file using ColdFusion

I've been working on a small project at work to allow a user to click a link from an event registration confirmation email, which will open a .ics file in the browser. This special file is in the iCalendar format, and is recognized by MS Outlook, Mozilla Sunbird, and other calendaring tools. It creates an entry in your calendar, with a 30 minute reminder alert, to help you remember to attend the webinar event you registered for. I learned from the vCal() function on cflib.org, and will be submitting my new iCalUS() UDF soon. But here is the code now, as it takes some time to get verified for inclusion on the CFLib.org site.

Download the code in a zip file.

I wrote the function to work in the U.S. and account for daylight savings time. Hopefully developers outside the U.S. can adapt this code to fit their timezones accordingly. Here is the code used for the test submission form and cfcontent/cfheader tags for the following demo.

Change any field(s) below and submit to generate a calendar file: <b>newAppointment.ics</b>
<P>
<cfoutput>
<form method="post">
<table>
<tr>
   <td align="right">Organizer name</td>
   <td><input type="Text" name="on" value="#Form.on#" size="30"></td>
</tr>
<tr>
   <td align="right">Organizer email</td>
   <td><input type="Text" name="oe" value="#Form.oe#" size="30"></td>
</tr>
<tr>
   <td align="right">Description</td>
   <td><input type="Text" name="desc" value="#Form.desc#" size="60"> (use \n sequences for newlines)</td>
</tr>
<tr>
   <td align="right">Subject</td>
   <td><input type="Text" name="sub" value="#Form.sub#" size="30"></td>
</tr>
<tr>
   <td align="right">Location</td>
   <td><input type="Text" name="loc" value="#Form.loc#" size="30"></td>
</tr>
<tr>
   <td align="right">Start Date/Time</td>
   <td><input type="Text" name="st" value="#Form.st#" size="20"> (format: <b>m/d/yyyy HH:mm</b> OR <b>h:mm TT</b> -- this is Eastern time)</td>
</tr>
<tr>
   <td align="right">End Date/Time</td>
   <td><input type="Text" name="et" value="#Form.et#" size="20"> (format: <b>m/d/yyyy HH:mm</b> OR <b>h:mm TT</b> -- this is Eastern time)</td>
</tr>
</table>
<input type="Submit" name="Submit" value="Submit">
</form>
</cfoutput>

<cfif IsDefined("Form.Submit")>
   <cfset eventStr = StructNew()>
   <cfset eventStr.organizerName = Form.on>
   <cfset eventStr.organizerEmail = Form.oe>   
   <cfset eventStr.startTime = ParseDateTime(Form.st)>
   <cfset eventStr.endTime = ParseDateTime(Form.et)>
   <cfset eventStr.subject = Form.sub>
   <cfset eventStr.location = Form.loc>
   <cfset eventStr.description = Form.desc>
   <cfcontent type="text/calendar" reset="Yes">
   <cfheader name="Content-Disposition" value="inline; filename=newAppointment.ics"><cfoutput>#iCalUS(eventStr)#</cfoutput>
</cfif>

Here is a demo of this in action.

-- Update 4/10/08: I submitted the UDF to cflib.org today. Hopefully Ray will post it soon. --

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Michael Santoroski's Gravatar This looks great, but there seems to be some weird characters in your code, and I can't find it on cflib.org. Can you send me the cfc? Thanks!
# Posted by Michael Santoroski | 4/9/08 4:00 PM
Troy's Gravatar It's in the queue for cflib.org inclusion. The last function I submitted took so long I had to ping Ray to finally get it posted. Might need to do that again, but I just submitted to that site last week on 4/10.
# Posted by Troy | 4/15/08 3:38 PM
Paul Riker's Gravatar Sweet work! Thanks!

Go Whitesox!!
# Posted by Paul Riker | 6/27/08 4:50 PM
Jason Dean's Gravatar Awesome post. Thanks Troy. This will be useful.
# Posted by Jason Dean | 7/8/08 2:09 PM
john's Gravatar hi,
thanks for this great post. just curious, how can we add additional fields so that we can add some event details? we want to integrate into outlook
# Posted by john | 7/9/08 9:19 AM
haylo75's Gravatar I just checked cflib.org and couldn't find this UDF on there :( Looks good though, I may have an application for something similar.
# Posted by haylo75 | 7/22/08 1:23 PM
Jim Priest's Gravatar Any chance I could get a copy of this? 4 months later and I can't find this on CFLib...
# Posted by Jim Priest | 8/10/08 2:50 PM
Jason Yin's Gravatar Hi
I was trying to put your code to our website. when I run it, I can create the iCal file, but when I open it, I got a error to import ti the outlook.
The error message: This error can appear if you have attempted to save a recurring Lunar appointment in iCalendar format.
To avoid this error, set the appointment option to Gregorian instead of Lunar.
here is my test website:
http://1bosdev1.conferon.com/demo/demo2007/Agenda/...
Please help me. We would like to use your code in our application.

Thanks
Jason
# Posted by Jason Yin | 9/17/08 10:53 AM
Troy's Gravatar I've seen a few folks here looking for the code. It has indeed made it to cflib now. But I also posted it within the blog entry itself, easy to overlook. This is the link:
http://blog.webdh.com/demos/iCalUS.zip
# Posted by Troy | 11/12/08 10:38 PM
Troy's Gravatar Here is a nice tool to validate iCalendar objects. Thanks to Dan R for finding a few fixes and posting soon to cflib.
http://severinghaus.org/projects/icv/
# Posted by Troy | 12/16/08 11:55 AM
uranio255's Gravatar I used the validator you mentioned with the .ics files generated on this page. It keeps saying that they are all invalid! weird
# Posted by uranio255 | 12/18/08 2:45 AM
Robert's Gravatar Hi Troy! Thanks for the great tag. Can you tell me how to modify it slightly to default to Central Time?

Thanks,
Robert
# Posted by Robert | 1/27/09 10:17 AM
Robert's Gravatar Troy, I think I figured out the Central Time zone question. One other question is how do I make an appt an "All Day" appointment?

Thanks,
Robert
# Posted by Robert | 1/27/09 11:14 AM
kristine's Gravatar I'm having the same issue as Jason above. I say the same thing, it looks great but I'm getting an error about it trying to use Gregorian rather than Lunar. I didn't see a post with the solution for this.

Any suggestions? I really want to use this code but I seem to be tripping at the finish line when it goes to save to Outlook.
# Posted by kristine | 2/23/09 6:42 PM
JanSR's Gravatar I have the same problem with the Gregorian/Lunar Calendar. Fun thing is: If you save the generated ics file and doubleclick it. it works a charm.

The problem seems to be in the "direct" delivery via cfcontent.
I've tried the exact same setup with IIS/CF7 (successful) and Apache2/Railo (no success)

Verified the mime definition of ics in both servers,
IIS: ics=applicatin/octet-stream
Ap: ics=text/calendar
modified the Mimetypes in Apache to match that of IIS -> Still no success
# Posted by JanSR | 3/5/09 3:32 AM
Troy's Gravatar On 3/27, Andrew M asked:
Wondered if you could explain to me how to adjust the timezone of the iCalUS() script. Specifically to default to pacific time. I also had a hard time decyphering how the time offset for daylight savings works. Thanks in advance, very useful function.

On 3/30, I replied back with:
In the code, these values are setup for Eastern time
vCal = vCal & "TZOFFSETFROM:-0500" & CRLF;
vCal = vCal & "TZOFFSETTO:-0400" & CRLF;

so to modify to Pacific, I think you'd change to this (2 spots in the code)
vCal = vCal & "TZOFFSETFROM:-0800" & CRLF;
vCal = vCal & "TZOFFSETTO:-0700" & CRLF;

The DST should just work, it uses the logic (copied from Wikipedia) which started in 2007, most of the United States and Canada observe DST from the second Sunday in March to the first Sunday in November. This corresponds to these 2 lines of code.
vCal = vCal & "RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SU;BYMONTH=3" & CRLF;
vCal = vCal & "RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=11" & CRLF;
# Posted by Troy | 3/30/09 10:40 AM
Andrew M's Gravatar Thanks, that helped. I should have seen that from the code - have struggled with this since they changed the daylight savings 2 weeks earlier and later.
# Posted by Andrew M | 5/22/09 5:35 PM
ja's Gravatar Troy, I tried out your Demo page and this is a great tool. Question I have is how to integrate my existing query into the variable fields?
I have added my query to the top of the page, but keep getting "Element ON is undefined in FORM."

Here is my code:
----------------------
<cfquery name="rsEmailCallDataOL" datasource="salesPipeline">
SELECT tblCallTracker.wkClsCallRem, tblCallTracker.wkClsCallBack, tblWeeklyRpt.wkRptID, tblCallTracker.wkClsEntDate, tblCallTracker.wkClsNum, tblWeeklyRpt.wkRptCustName, tblCallTracker.id_ClsWkRpt, tblCallTracker.wkClsID, user_usr.name_usr, user_usr.email_usr, tblWeeklyRpt.wkRptCustCity, tblWeeklyRpt.wkRptContact
FROM ((tblCallTracker
LEFT JOIN tblWeeklyRpt ON tblWeeklyRpt.wkRptID=tblCallTracker.id_ClsWkRpt)
INNER JOIN user_usr ON user_usr.id_usr=tblWeeklyRpt.wkRptUser)
WHERE tblCallTracker.wkClsID = <cfqueryparam value="#URL.id_com#" cfsqltype="cf_sql_numeric">
</cfquery>

<b>newAppointment.ics</b>
<cfoutput>
<cfform method="post" name="formOL" id="formOL">
<table>
<tr>
<td align="right">Account Exec</td>
<td><cfinput type="Text" name="on" value="#rsEmailCallDataOL.name_usr#" size="30"></td>
</tr>
<tr>
<td align="right">Email</td>
<td><cfinput type="Text" name="oe" value="#rsEmailCallDataOL.email_usr#" size="30"></td>
</tr>
<tr>
<td align="right">Contact Name</td>
<td><cfinput type="Text" name="desc" value="#rsEmailCallDataOL.wkRptContact#" size="60">
(use \n sequences for newlines)</td>
</tr>
<tr>
<td align="right">Customer Name</td>
<td><cfinput type="Text" name="sub" value="#rsEmailCallDataOL.wkRptCustName#" size="30"></td>
</tr>
<tr>
<td align="right">Phone Number</td>
<td><cfinput type="Text" name="loc" value="#rsEmailCallDataOL.wkClsNum#" size="30"></td>
</tr>
<tr>
<td align="right">Called Date/Time</td>
<td><cfinput type="Text" name="st" value="#rsEmailCallDataOL.wkClsEntDate#" size="20">
(format: <b>m/d/yyyy HH:mm</b> OR <b>h:mm TT</b> -- this is Eastern time)</td>
</tr>
<tr>
<td align="right">Call Back Date/Time</td>
<td><cfinput type="Text" name="et" value="#rsEmailCallDataOL.wkClsCallBack#" size="20">
(format: <b>m/d/yyyy HH:mm</b> OR <b>h:mm TT</b> -- this is Eastern time)</td>
</tr>
</table>
<cfinput type="Submit" name="Submit" value="Submit">
</cfform>
</cfoutput>

<cfif IsDefined("Form.Submit")>
<cfset eventStr = StructNew()>
<cfset eventStr.organizerName = Form.on>
<cfset eventStr.organizerEmail = Form.oe>
<cfset eventStr.startTime = ParseDateTime(Form.st)>
<cfset eventStr.endTime = ParseDateTime(Form.et)>
<cfset eventStr.subject = Form.sub>
<cfset eventStr.location = Form.loc>
<cfset eventStr.description = Form.desc>
<cfcontent type="text/calendar" reset="Yes">
<cfparam name="URL.id_com" default="-1">
<cfheader name="Content-Disposition" value="inline; filename=newAppointment.ics"><cfoutput>#iCalUS(eventStr)#</cfoutput>
</cfif>
----------------------------------
Basically, I just want the Form to open up, (similiar to your Demo), but with the Form fields already pre-filled already with my data from query?
Thanks,
ja
# Posted by ja | 1/28/10 5:06 PM
Simon's Gravatar Hi all, managed to send the vcalebdar event out to the attendees using cf9 and Lotus Notes 6. My question is how do I send it to the organizer so that it will appear in their calendar. My email doesnt seem to work when the to and from fields in the cfmail tag are the same as the organizer field of the VCALENDAR item in the body of the email,

thanks in advance,

Simon.
# Posted by Simon | 2/12/10 12:01 PM
Elena Aminova's Gravatar I am using the iCalUS and it opens up the ics file in the browser.
I need it to not open in the browser, but be emailed...
What do I need to change in the following block of code to automatically email the generated .ics file??
<cfcontent type="text/calendar" reset="Yes">
<cfheader name="Content-Disposition" value="filename=newAppointment.ics" >
<cfoutput>#iCalUS(eventStr)#</cfoutput>
Thanks!
# Posted by Elena Aminova | 3/9/10 10:49 AM
Simon's Gravatar Hi there, heres an extract of my code which I finally got working with CF9 and Lotus Notes :

<!--- build to list --->

<cfset toEmail = "">
<cfloop query="users">
   <cfset toEmail = toEmail & users.email_address & ",">
</cfloop>

<!--- create message body --->
<cfset myMessage = "BEGIN:VCALENDAR"&chr(13)&chr(10)>
<cfset myMessage = myMessage & "VERSION:2.0"&chr(13)&chr(10)>
<cfset myMessage = myMessage & "METHOD:REQUEST"&chr(13)&chr(10)>
<cfset myMessage = myMessage & "BEGIN:VEVENT"&chr(13)&chr(10)>
<!--- use a sequence number to let notes know if this is the first occurence of this event --->
<cfset myMessage = myMessage & "SEQUENCE:"&#event.sequence#&chr(13)&chr(10)>
<cfset myMessage = myMessage & "ORGANIZER;CN=Common Name for Organizer:MAILTO:email address for organizer"&chr(13)&chr(10)>
<cfloop query="users">
<cfset myMessage = myMessage & "ATTENDEE;RSVP=YES;TYPE=INDIVIDUAL;MAILTO:"&#users.email_address#&chr(13)&chr(10)>
</cfloop>
<cfset myMessage = myMessage & "DTSTART:"&#DateFormat(startDate,"YYYYMMDD")#&"T"&#FORM.startHours#&#FORM.startMinutes#&"00Z"&chr(13)&chr(10)>
<cfset myMessage = myMessage & "DTEND:"&#DateFormat(endDate,"YYYYMMDD")#&"T"&#FORM.endHours#&#FORM.endMinutes#&"00Z"&chr(13)&chr(10)>
<!--- in notes each event must have a unique id --->
<cfset myMessage = myMessage & "UID:"&#event.uniqueEventId#&chr(13)&chr(10)>
<cfset myMessage = myMessage & "LOCATION:"&#FORM.location#&chr(13)&chr(10)>
<cfset myMessage = myMessage & "DESCRIPTION:"&#FORM.description#&chr(13)&chr(10)>
<cfset myMessage = myMessage & "SUMMARY:"&#FORM.calendarName#&chr(13)&chr(10)>
<cfset myMessage = myMessage & "PRIORITY:3"&chr(13)&chr(10)>
<cfset myMessage = myMessage & "STATUS:CONFIRMED"&chr(13)&chr(10)>
<cfset myMessage = myMessage & "END:VEVENT"&chr(13)&chr(10)>
<cfset myMessage = myMessage & "END:VCALENDAR"&chr(13)&chr(10)>

<!--- trim the last comma off the end --->
<cfset toEmail = #Left(toEmail,Len(toEmail)-1)#>

<!--- send out invites --->
<cfmail to="#toEmail#" from="generic account <generic email>" subject="Calendar Entry" type="multipart">
<cfmailparam name="content-class" value="urn:content-classes:calendarmessage">
<cfmailparam name="content-type" value="text">
<cfmailparam name="charset" value="utf-8">
<cfmailparam name="content-transfer-encoding" value="7bit">
<cfmailpart type="text/calendar">
      <cfoutput>#myMessage#</cfoutput>
</cfmailpart>
</cfmail>
# Posted by Simon | 3/10/10 7:51 AM
Elena's Gravatar Would it work with CF8? Thanks.
# Posted by Elena | 3/10/10 8:30 AM
Simon's Gravatar I dont see why not :o)
# Posted by Simon | 3/10/10 11:50 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.002. Contact Blog Owner