Home > PHP > Creating iCalendar (ics) files with PHP

Creating iCalendar (ics) files with PHP

January 2nd, 2008 Leave a comment Go to comments

For a past project, I needed to create iCalendar files from event data stored in a database. There is an iCalendar class on phpclasses.org, but there is very little documentation and you need to understand some of the quirks of the iCalendar specification for it to be useful.

Since I got stumped on trying to get the class working, I went to the specs and figured out how to do it without using the class. Here are some links that I found helpful:

An iCalendar file is just a text file, so creating it is not difficult – it’s figuring out the quirks of the specification and understanding what the different properties mean that is frustrating. Of course the other frustrating thing is that there seem to be differences in how calendar applications parse ics files, and which fields they consider to be required.

Here’s the MySQL Table Schema that I’m using for my calendar table:

CREATE TABLE calendar
(
     id int(11) NOT NULL auto_increment,
     title tinytext,
     description text,
     startDate date NOT NULL default '0000-00-00',
     endDate date NOT NULL default '0000-00-00',
     PRIMARY KEY (id)
)

And here’s the PHP that I used. Note that this code currently does not export ics files that can be used by Mozilla Sunbird. It works with MS Outlook 2003, which is my current target client.

// database connection
include_once('db.php');
// Dates must be in the format YYYYMMDD
$events = $conn->execute("
	SELECT 
		id, 
		title, 
		DATE_FORMAT(startDate,'%Y%m%d') AS start, 
		DATE_FORMAT(DATE_ADD(endDate, INTERVAL 1 DAY),'%Y%m%d') AS end, 
		startDate, 
		endDate, 
		description 
	FROM 
		calendar");
// Define the file as an iCalendar file
header("Content-Type: text/Calendar");
// Give the file a name and force download
header("Content-Disposition: inline; filename=calendar.ics");
// Header of ics file
echo "BEGIN:VCALENDAR\n";
echo "VERSION:2.0\n";
echo "PRODID:PHP\n";
echo "METHOD:REQUEST\n";
// Loop through database results and create an event for each item
while(!$events->EOF)
{
    echo "BEGIN:VEVENT\n";
    // The end date of an event is non-inclusive, so if the event is an all day event or one with no specific start and stop
    // times, the end date would be the next day.  This script is used with a calendar that does not deal with times,
    // just dates, so the time for all events is set to 000000.
    echo "DTSTART:".$events->fields[start]."T000000Z\n";
    echo "DTEND:".$events->fields['end']."T000000Z\n";
    // Only create Description field if there is a description
    if(isset($events->fields[description]) && $events->fields[description] != '')
    {
            echo "DESCRIPTION:";
            // Remove all linebreaks from description stored in database
            $description = str_replace(chr(13).chr(10),"  ", $events->fields[description]);
            echo $description."\n";
    }
    echo "SUMMARY:{$events->fields[title]}\n";
    echo "UID:{$events->fields[id]}\n";
    echo "SEQUENCE:0\n";
    echo "DTSTAMP:".date('Ymd').'T'.date('His')."\n";
    echo "END:VEVENT\n";
    $events->MoveNext();
}
echo "END:VCALENDAR\n";
Categories: PHP Tags: , ,
  1. Joe
    February 21st, 2008 at 13:34 | #1

    The link below is broken. It is the third link the list on top.

    link text: ‘# Entire iCalendar Specification’ link: ‘http://rfc.net/rfc2445.html’

  2. Bwhendrickx
    March 11th, 2008 at 07:19 | #2

    Very nice code, but a little bit vague. Which code contains the db.php?? only username, password, etc.? what is: $conn-&gt
    because when I execute: Parse error: syntax error, unexpected ‘&’ in /xxxxx/xxxx/xxx/generator.php on line 6

  3. April 28th, 2008 at 20:45 | #3

    Bwhendrickx:

    db.php is where the connection parameters are stored. In this case I was using adodb (http://adodb.sf.net) so $conn is the connection object. Here’s the contents of db.php

    < ?php
    include('adodb.inc.php');
    $conn = NewADOConnection('mysql');
    $conn->connect(’localhost’, ‘username’, ‘password’, ‘database’);
    ?>

    Also, the &gt should have been > — WordPress decided to convert it to the HTML entity for me and leave off the semi-colon.. how helpful.

  4. April 28th, 2008 at 20:46 | #4

    Joe: Thanks, but the link seems to be working fine now. Maybe their site was down temporarily.

  5. Nathan
    July 22nd, 2008 at 02:36 | #5

    Thanks for this, it’s really cleared things up for me. I was planning on doing this for a project further down the line but now I can get it out there at the start. Cheers
    Nathan

  6. Christian Asche
    August 28th, 2008 at 01:53 | #6

    Thank you for the tutorial.

    I got an error in Outlook 2007: 0×00040023 Der Internetkalender enthält einen fließenden DTStart Wert …

    This fix worked for me:

    echo “DTSTART:”.$events->fields[start].”T000000Z\n”;
    echo “DTEND:”.$events->fields['end'].”T000000Z\n”;

    I just added an Z to DTSTART and DTEND, to get rid of the warning.

  7. November 10th, 2008 at 06:23 | #7

    Thanks for the headstart. I need to code something like this for a client who wants to add events to her iPhone, so I think an ical file is required. Do you know of any iPhone specific issues to be aware of? Unfortunately I don’t have an iPhone either, so any clue how I can test it?

  8. bradym
    November 20th, 2008 at 09:55 | #8

    Tom:

    Sorry for the delayed response. I don’t have an iPhone, so I haven’t played around with adding events to it. My guess is that if it works in iCal on OS X it will work for the iPhone, but I have no documentation to support that theory.

  9. Simon
    December 11th, 2008 at 01:35 | #9

    Hey,

    What is the ‘MoveNext’ statement: $events->MoveNext();
    As it gives the error message “Fatal error: Call to a member function MoveNext() on a non-object” ?

    So far so good though !

    • bradym
      December 11th, 2008 at 22:26 | #10

      Simon:

      $events->MoveNext() is part of the adodb database abstraction library that just moves to the next item in the list. If you’re not using adodb, you’ll need to strip it out, along with other references to the adodb library. One of these days I’ll modify the code to work without adodb.

      Brady

  10. Steve
    December 22nd, 2008 at 15:47 | #11

    Thank you for the comment:
    // The end date of an event is non-inclusive, so if the event is an all day event or one with no specific start and stop
    // times, the end date would be the next day. This script is used with a calendar that does not deal with times, just dates,
    // so the time for all events is set to 000000.

    Does anyone else think this is a bit wonky? Just to translate, what they’re saying is, if you tell iCal “12/4-12/6″, it will interpret that to mean “12/4-12/5″. It’s already some unnecessarily overcomplicated VCARD style format – do we really need to gild the lilly with that logical nightmare?

  11. January 15th, 2009 at 02:18 | #12

    Hello,

    This creates an ICS file, right? I was wondering how this can be done like google calendar. In GC, there’s no attachment sending invitation from GC to outlook gives you the accept/deny buttons on the top directly. Any idea how to do that?

    Thanks.
    Noor

    • bradym
      March 15th, 2009 at 20:56 | #13

      @Christian: Thanks! I’ve updated the post to reflect your fix.

      @Steve: I agree completely! It’s very confusing and definitely not intuitive!

      @Noor: I think maybe Outlook hides the ICS files, because when I get Google calendar invites and view them in the Mail program on my Mac, I see an attachment in ICS format.

      @Foilking: Take a look at the code comment just inside the while loop, the one Steve mentioned in his comment on this post. Set the end date to the next day.

  12. Foilking
    March 11th, 2009 at 09:11 | #14

    I’m trying to make an event an all day event. How would I set the time value to tell Outlook that it is an all day event?

  13. Andrew
    March 27th, 2009 at 03:04 | #15

    Sorry if I am being a bit slow but…

    What happens when you set up an event, the user downloads the event, saves it to their calendar and then the admin cancels that event a week later?? Surely the event in the users calendar would still be showing that it was still on even though it wasn’t?

    Is there any way of getting the calendar to read from an external ics file rather than having the user download it or would that be a security problem/not possible?

  14. Noah
    May 9th, 2009 at 19:31 | #16

    Yeah, I’m looking to do something like this as well.

    I need to send an “initial” meeting invite.
    A meeting “update”.
    And a meeting “cancellation” notice.

  15. subface
    May 30th, 2009 at 23:35 | #17

    thx for the great code. it’s working perfectly for my similar need except the timezone is off since it doesn’t appear to be set in the file.

    any idea what the code for adding that would be?

    much appreciated for any help, and if no idea what you already did was super helpful!