[Vobject] Example of using timezones

Jeffrey Harris jeffrey at skyhouseconsulting.com
Fri Jul 2 11:48:36 CDT 2010


Hi Richard,

> I am sure this is very simple, but I have been trying for too long to
> work out the correct way to generate an iCalendar with a correct
> timezone - so I thought I would ask for an example. I can't be the only
> one that has struggled to work out how to do it.
> 
> I am generating the calendar for the EuroPython 2010 conference
> (http://www.europython.eu/talks/timetable/). The event times are in
> British Summer Time (GMT+1). I just need to express this correctly in
> the ics file.
> 
> Could someone show me a simple example of creating the iCalendar and
> telling it that all times are in BST?

Timezones are a royal pain in the ass, there's just no getting around it.

There are two routes you could pursue. The first would be to just express everything in UTC, then you don't have any dependence on complicated tzinfo classes.

That's going to have varied display results, depending on the client. Almost all clients will display the resulting events at the right locations on their canvas, but most will show a different time than the one you expect in event details. So, when possible, it's much better to use an accurate tzinfo class. The trick is you need a library that provides timezones (tzinfo classes).

vobject attempts to be agnostic on these, but in the end some clients do a bad job parsing any VTIMEZONE whose TZID is not an Olson string, something like "America/Los_Angeles". Unfortunately, the tzinfo class doesn't define a tzid (it defines tzname, which is a DST dependent name, e.g. will give either PST or PDT, depending on the date passed in), and different tzinfo libraries use different patterns. PyICU uses an attribute named "tzid", pytz uses "zone", and dateutil's tzical uses "_tzid". These three patterns are currently understood by vobject.

So, if you want to avoid adding any dependencies and you're going to use a tzinfo class, probably the easiest thing to do is find a nice VTIMEZONE for "Europe/London", parse it with dateutil's tzical. Unfortunately, tzical has various irritations: it wants to read a filename or stream (not that big a deal, use StringIO), and worse, it will fail hard if it gets any lines it doesn't recognize. vobject uses tzical and works around this issue by parsing vtimezones, re-serializing a sanitized version, then parsing with tzical, which is lame but works fine in practice.

So, finally, here's working code, (assuming the random VTIMEZONE I randomly grabbed off the internet is accurate for Europe/London):

>>> import dateutil
>>> from cStringIO import StringIO
>>>
>>> sanitized_london_vtimezone = """BEGIN:VTIMEZONE
... TZID:Europe/London
... BEGIN:DAYLIGHT
... TZOFFSETFROM:+0000
... TZOFFSETTO:+0100
... TZNAME:BST
... DTSTART:19700329T010000
... RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3
... END:DAYLIGHT
... BEGIN:STANDARD
... TZOFFSETFROM:+0100
... TZOFFSETTO:+0000
... TZNAME:GMT
... DTSTART:19701025T020000
... RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10
... END:STANDARD
... END:VTIMEZONE"""
>>> buffer = StringIO(sanitized_london_vtimezone)
>>> buffer.seek(0)
>>> london_tz = dateutil.tz.tzical(buffer).get()
>>> london_tz
<tzicalvtz 'Europe/London'>

And, here's an example of using your hard-earned tzinfo class:

>>> import vobject
>>> from datetime import datetime
>>> cal = vobject.iCalendar()
>>> cal.add('vevent').add('title').value = "test event"
>>> cal.vevent.add('dtstart').value = datetime(2010,8,1,3, tzinfo=london_tz)
>>>
>>> print cal.serialize()
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//PYVOBJECT//NONSGML Version 1//EN
BEGIN:VTIMEZONE
TZID:Europe/London
BEGIN:STANDARD
DTSTART:20001029T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZNAME:GMT
TZOFFSETFROM:+0100
TZOFFSETTO:+0000
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:20000326T010000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
TZNAME:BST
TZOFFSETFROM:+0000
TZOFFSETTO:+0100
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
UID:20100702T164316Z-58541 at jharris
DTSTART;TZID=Europe/London:20100801T030000
TITLE:test event
END:VEVENT
END:VCALENDAR

Hope that helps!
Jeffrey





More information about the VObject mailing list