TheGeekery

The Usual Tech Ramblings

Wrapping my head around iCal

iCal is a specification used to define the format of a calendar. It’s an RFC standard. I started tinkering with it a few years back with SquirrelMail, but my curiosity about it was again peeked when I decided to try figuring a way to sync my new toy with Google Calendar

I had found a few apps that already synchronized Google Calendar, and other googly stuff with WM6, however, as I know it works with Microsoft’s Compact Framework, I figured I could probably figure something out myself.

One of the examples I found included source code so I had a brief insight into what they were doing. I stumbled across 2 bugs pretty quickly. Well, one was a bug, the other was a TODO item. The first bug seemed to be related around handling time zones. When I first ran the app, it pushed all of my events to Google +5 hours earlier than they were on my phone’s calendar. I figured this was due to timezone funkyness. The TODO item was reoccurances. This made it a bit of a deal breaker for me, as I use reoccurances a lot (pay credit card, go to gym, etc). This inspired me to once again, start digging into APIs over at Google.

Google have a relatively well documented set of APIs, the Calendar API is documented here. The really great thing about Google, is they provide a series of developer tools (SDKs) that come in a variety of languages, included .NET. This is handy, as it saves developers having to handle a lot of stuff themselves.

For the most part, Google’s SDK is pretty complete, however I found it didn’t seem to explode the recurrence in a way I’d have expected, and in fact just left the section entirely as specified in the RFC. This seemed a little odd, but at the same time, probably saved them a little work. So while I was looking at one of my entries from my Calendar, I had to decipher what it all meant. I’m used to reading RFCs, but I must admit, this had me a little confused at first…

Here is my recurrence entry for paying my credit card1.

DTSTART;TZID=America/Chicago:20070516T210000
DTEND;TZID=America/Chicago:20070516T220000
RRULE:FREQ=MONTHLY;WKST=SU;BYMONTHDAY=16
BEGIN:VTIMEZONE
TZID:America/Chicago
X-LIC-LOCATION:America/Chicago
BEGIN:DAYLIGHT
TZOFFSETFROM:-0600
TZOFFSETTO:-0500
TZNAME:CDT
DTSTART:19700308T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:-0500
TZOFFSETTO:-0600
TZNAME:CST
DTSTART:19701101T020000
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
END:STANDARD
END:VTIMEZONE

To me, DTSTART, and DTEND were pretty obvious, it was the start, and end times of the event, however I was initially baffled by the fact there was multiple DTSTART entries, as well as RRULE entries. I then noticed multiple BEGINs too, this is where things started clicking for me…

DTSTART;TZID=America/Chicago:20070516T210000
DTEND;TZID=America/Chicago:20070516T220000
RRULE:FREQ=MONTHLY;WKST=SU;BYMONTHDAY=16

This line is telling my application that the event starts on TZID (timezone ID) “American/Chicargo”, on May 16th, 2007, at 2100 (9pm for normal people), and ends at 2200 (10pm) on the same day. The RRULE line tells the application that it is reoccuring every month on the 16th. The WKST portion tells us that its reporting the week starts on a Sunday.

However, being smart, not all applications know what “American/Chicargo” means as a time zone. This is where the mass of the rest of the information comes from…

BEGIN:VTIMEZONE
TZID:America/Chicago
X-LIC-LOCATION:America/Chicago
BEGIN:DAYLIGHT
TZOFFSETFROM:-0600
TZOFFSETTO:-0500
TZNAME:CDT
DTSTART:19700308T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:-0500
TZOFFSETTO:-0600
TZNAME:CST
DTSTART:19701101T020000
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
END:STANDARD
END:VTIMEZONE

Smart readers would have noticed there is a START/END VTIMEZONE. That’s the iCal specification telling us to start and end a time zone definition. At the top of that start is a TZID so we can now match the TZID in the event up with the TZID in this timezone definition. As with SMTP headers, X-* headers are optional, and aren’t standard, but Google added one in to hint at a location with X-LIC-LOCATION. Then comes the next fun bit. I live in a region of the country that still observes daylight savings, which is what is being explained in the next bit…

BEGIN:DAYLIGHT
TZOFFSETFROM:-0600
TZOFFSETTO:-0500
TZNAME:CDT
DTSTART:19700308T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
END:DAYLIGHT

This tells the application that starting from March 8th, 1970, at 0200 (2am) we observe daylight savings. This is where the clocks shift back, and hence the TZOFFSETFROM is -0600 (-6 hours) from GMT. The RRULE section tells us this is a early event, happening on the second Sunday (BYDAY=2SU) of the third month (BYMONTH=3). It also gives it a friendly timezone name “CDT”.

There is then the reverse of this with the standard time…

BEGIN:STANDARD
TZOFFSETFROM:-0500
TZOFFSETTO:-0600
TZNAME:CST
DTSTART:19701101T020000
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
END:STANDARD

In this case, the event starts on November 1st at 0200. The RRULE says this occurs yearly on the 1st Sunday of the 11th month.

All this extra information now allows our application to understand the event, the timezones relating to the event, and the recurrences of the event. At first lots of stuff, but when you stare at it for a few minutes, it all starts to make perfect sense. Now to continue creating my application…

  1. If you feel nice, you can pay it for me, now I’ve told you when it’s due 

Comments