[Vobject] timezone bugs prevent round-tripping an ical object

Phil Mayers p.mayers at imperial.ac.uk
Tue May 2 10:20:51 CDT 2006


All,

In the process of writing a Google Calendar sync backend for opensync 
(using the python plugin) I ran into this.

I've got the following ical object extracted from Exchange over IMAP:

BEGIN:VCALENDAR
METHOD:REQUEST
PRODID:Microsoft CDO for Microsoft Exchange
VERSION:2.0
BEGIN:VTIMEZONE
TZID:(GMT) Greenwich Mean Time/Dublin/Edinburgh/London
X-MICROSOFT-CDO-TZID:1
BEGIN:STANDARD
DTSTART:16010101T020000
TZOFFSETFROM:+0100
TZOFFSETTO:+0000
RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:16010101T010000
TZOFFSETFROM:+0000
TZOFFSETTO:+0100
RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
DTSTAMP:20060424T105530Z
DTSTART;TZID="(GMT) Greenwich Mean Time/Dublin/Edinburgh/London":20060509T1
  40000
SUMMARY:Do some stuff
UID:040000008200E00074C5B7101A82E0080000000050DD4DFB9567C601000000000000000
  0100000004B9667D97B4A9F4BB895EEA4AAD5F72D
ORGANIZER;CN="User, Joe":MAILTO:joe.user at example.com
LOCATION:ME439
DTEND;TZID="(GMT) Greenwich Mean Time/Dublin/Edinburgh/London":20060509T160
  000
DESCRIPTION:\N
SEQUENCE:0
PRIORITY:5
CLASS:
CREATED:20060424T105533Z
LAST-MODIFIED:20060424T115645Z
STATUS:CONFIRMED
TRANSP:OPAQUE
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:REMINDER
TRIGGER;RELATED=START:-PT00H15M00S
END:VALARM
END:VEVENT
END:VCALENDAR

I'm parsing the fields of interest into a dictionary (which eventually 
will get sent to google as XML via the AtomPP but that's neither here 
nor there).

 >>> ge.attrs['start']
datetime.datetime(2006, 5, 9, 14, 0, tzinfo=<tzicalvtz '(GMT) Greenwich 
Mean Time/Dublin/Edinburgh/London'>)
 >>> ge.attrs['start'].tzinfo
<tzicalvtz '(GMT) Greenwich Mean Time/Dublin/Edinburgh/London'>
 >>> ge.attrs['start'].tzinfo.tzname(ge.attrs['start'])
 >>>

...and that last bit prevents re-serialisation to ical, which I'm 
testing with - the code breaks with a traceback:

 >>> ge.toIcal()
Traceback (most recent call last):
   File "<stdin>", line 1, in ?
   File "gdata.py", line 187, in toIcal
     return nc.serialize()
   File "SITE-PACKAGES/vobject/base.py", line 180, in serialize
     out = self.behavior.serialize(transformed, buf, lineLength)
   File "SITE-PACKAGES/vobject/behavior.py", line 134, in serialize
     return base.defaultSerialize(obj, buf, lineLength)
   File "SITE-PACKAGES/vobject/base.py", line 776, in defaultSerialize
     child.serialize(outbuf, lineLength, validate=False)
   File "SITE-PACKAGES/vobject/base.py", line 180, in serialize
     out = self.behavior.serialize(transformed, buf, lineLength)
   File "SITE-PACKAGES/vobject/behavior.py", line 134, in serialize
     return base.defaultSerialize(obj, buf, lineLength)
   File "SITE-PACKAGES/vobject/base.py", line 776, in defaultSerialize
     child.serialize(outbuf, lineLength, validate=False)
   File "SITE-PACKAGES/vobject/base.py", line 180, in serialize
     out = self.behavior.serialize(transformed, buf, lineLength)
   File "SITE-PACKAGES/vobject/behavior.py", line 134, in serialize
     return base.defaultSerialize(obj, buf, lineLength)
   File "SITE-PACKAGES/vobject/base.py", line 776, in defaultSerialize
     child.serialize(outbuf, lineLength, validate=False)
   File "SITE-PACKAGES/vobject/base.py", line 185, in serialize
     return defaultSerialize(self, buf, lineLength)
   File "SITE-PACKAGES/vobject/base.py", line 792, in defaultSerialize
     s.write(':' + obj.value)
TypeError: cannot concatenate 'str' and 'NoneType' objects

pdb says:

 >>> import pdb
 >>> pdb.pm()
 > SITE-PACKAGES/vobject/base.py(792)defaultSerialize()
-> s.write(':' + obj.value)
(Pdb) obj
<TZNAME{}None>
(Pdb) up
 > SITE-PACKAGES/vobject/base.py(185)serialize()
-> return defaultSerialize(self, buf, lineLength)
(Pdb) up
 > SITE-PACKAGES/vobject/base.py(776)defaultSerialize()
-> child.serialize(outbuf, lineLength, validate=False)
(Pdb) print obj
<STANDARD| [<DTSTART{}2000-10-29 02:00:00>, 
<RRULE{}FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10>, <TZNAME{}None>, 
<TZOFFSETFROM{}+0100>, <TZOFFSETTO{}+0000>]>

I confess I find the vobject code impenetrable, so I can't really debug 
it, but I guess what happens is this:

  1. The ical is parsed and a dateutil.tz.tzicalvtz instance generated 
for the VTIMEZONE objects. The subsequent DTSTART and DTEND parsers 
lookup this timezone info and attach it to the datetime objects. The 
instance for the above file will not have a TZNAME attribute.

  2. TimezoneComponent.settzinfo runs through the list of rules, and 
unconditionally adds a "name" key to the "rule" dict 
(vobject/icalendar.py:139) then unconditionally adds the TZNAME element 
to the DAYLIGHT/STANDARD elements, setting the value to the
return value of tzinfo.tzname(date) - which is None. The serialisation 
then fails.

I'm guessing this is the correct patch:

--- icalendar.py~       2006-05-02 16:16:20.000000000 +0100
+++ icalendar.py        2006-05-02 16:15:54.000000000 +0100
@@ -214,7 +214,8 @@
                  comp = self.add(transitionTo)
                  dtstart = comp.add('dtstart')
                  dtstart.value = rule['start']
-                comp.add('tzname').value  = rule['name']
+                if rule['name']:
+                    comp.add('tzname').value  = rule['name']
                  line = comp.add('tzoffsetto')
                  line.value = deltaToOffset(rule['offset'])
                  line = comp.add('tzoffsetfrom')

...but I don't know enough about the standard to be sure that's right.


More information about the VObject mailing list