[Vobject] Example of using timezones
Richard Taylor
rjt-pyconuk at thegrindstone.me.uk
Sat Jul 3 03:58:50 CDT 2010
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On 02/07/2010 17:48, Jeffrey Harris wrote:
> 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
Thanks Jeff. I don't feel so bad now about not being able to work it out
now :-)
Interestingly, even when I include the timezone information (following
your instructions), Google Calendar still does not accept that the times
are meant to be in British Summertime (Daylight saving).
Here is an example entry:
BEGIN:VEVENT
UID:0c879fc5d8a6f6736468e12e355a9831 at europython.org
DTSTART;TZID=Europe/London:20100717T090000
DURATION:PT15M
SUMMARY:Tutorial Registration and Coffee - Tutorial Day 1
END:VEVENT
The timezone information looks like this:
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
I have also included a X-WR-TIMEZONE, as a bit of Googling suggests that
this might be required:
X-WR-TIMEZONE:Europe/London\;+00:00\;London\;
But it does not make any difference.
The ics file works fine when imported into iCal on my Mac, but when
imported into Google Calendar it thinks that the times are GMT+0 so they
are all 1 hour out!
Is this a problem with Google Calendar or do I need to do something
explicit to state that the event DTSTART times are meant to be Daylight
Saving?
Thank you again for your superb support.
All the best
Richard
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (Darwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iD8DBQFMLvvK7Z7YaKfan9kRArCsAKCEd7Pkr5bF5A92uV/kecTqCljCIgCdHwyX
NcGI7F6cHVjYdLok1OEMoPs=
=7b28
-----END PGP SIGNATURE-----
More information about the VObject
mailing list