1. Doug Hellmann
  2. ical2org
  3. Issues
Issue #7 new

TimeZone support

Ken Mankoff
created an issue

I have an event assigned to a timezone in iCal. I have my computer set to a different timezone. ical2org assigns the event to 'tz/localtime'.

Here is the 'foo' event: {{{ BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Apple Inc.//iCal 4.0.4//EN CALSCALE:GREGORIAN BEGIN:VEVENT CREATED:20120803T234104Z UID:10480F86-2BE3-48CD-AF07-40D03B2EED4D DTEND;TZID=America/Los_Angeles:20120928T110000 TRANSP:OPAQUE X-APPLE-DONTSCHEDULE:TRUE SUMMARY:Foo DTSTART;TZID=America/Los_Angeles:20120928T090000 DTSTAMP:20120926T170213Z X-APPLE-EWS-BUSYSTATUS:BUSY SEQUENCE:3 END:VEVENT END:VCALENDAR }}}

And here is the parsing of it if I run 'ical2org --verbose'

{{{ checking 2012-09-28 09:00:00 - 2012-09-28 11:00:00 == Foo for <UID{}10480F86-2BE3-48CD-AF07-40D03B2EED4D> Adding tzfile('/etc/localtime') time zone to 2012-09-28 09:00:00 value after change: 2012-09-28 09:00:00+02:00 Adding tzfile('/etc/localtime') time zone to 2012-09-28 11:00:00 value after change: 2012-09-28 11:00:00+02:00 Foo event_start 2012-09-28 09:00:00+02:00 event_end 2012-09-28 11:00:00+02:00 day_span 0:00:00 partial day event single day ** Foo <2012-09-28 Fri 09:00-11:00> :PROPERTIES: UID: 10480F86-2BE3-48CD-AF07-40D03B2EED4D :END: }}}

Comments (6)

  1. Ken Mankoff reporter

    The following patch is an improvement... But it seems things are still off by an hour now. Will post improved patch when I debug this.

    diff -r d2d5da0e2607 ical2org/filter.py
    --- a/ical2org/filter.py	Tue Jan 03 08:37:29 2012 -0500
    +++ b/ical2org/filter.py	Fri Sep 28 13:11:28 2012 +0200
    @@ -11,6 +11,7 @@
     import sys
     
     import vobject
    +import pytz
     
     from ical2org import tz
     
    @@ -19,10 +20,15 @@
     def by_date_range(events, start, end):
         """Iterate over the incoming events and yield those that fall within the date range.
         """
    +
         local_start = start.astimezone(tz.local)
         local_end = end.astimezone(tz.local)
         log.debug('filtering between %s (%s) and %s (%s)', start, local_start, end, local_end)
         for event in events:
    +        if event.dtstart.params.get('X-VOBJ-ORIGINAL-TZID') != None:
    +            event_tz = pytz.timezone(event.dtstart.params['X-VOBJ-ORIGINAL-TZID'][0])
    +            event.dtstart.value = event.dtstart.value.replace(tzinfo=event_tz)		
    +            event.dtend.value = event.dtend.value.replace(tzinfo=event_tz)		
             log.debug('checking %s - %s == %s for %s',
                       event.dtstart.value,
                       event.dtend.value,
    
  2. Ken Mankoff reporter

    OK I think I got it. Not easy since I'm not really a Python programmer, but this is good practice. I've also fixed a documentation bug.

    diff -r d2d5da0e2607 ical2org/app.py
    --- a/ical2org/app.py	Tue Jan 03 08:37:29 2012 -0500
    +++ b/ical2org/app.py	Sat Sep 29 19:25:32 2012 +0200
    @@ -121,7 +121,7 @@
                                  type='choice',
                                  choices=FORMATTER_FACTORIES.keys(),
                                  default='org',
    -                             help='Output format. One of %s. Defaults to "diary".' % FORMATTER_FACTORIES.keys(),
    +                             help='Output format. One of %s. Defaults to "org".' % FORMATTER_FACTORIES.keys(),
                                  )
         option_parser.add_option('--opt', '--formatter-option',
                                  action='callback',
    diff -r d2d5da0e2607 ical2org/filter.py
    --- a/ical2org/filter.py	Tue Jan 03 08:37:29 2012 -0500
    +++ b/ical2org/filter.py	Sat Sep 29 19:25:32 2012 +0200
    @@ -11,6 +11,7 @@
     import sys
     
     import vobject
    +import pytz
     
     from ical2org import tz
     
    @@ -23,6 +24,15 @@
         local_end = end.astimezone(tz.local)
         log.debug('filtering between %s (%s) and %s (%s)', start, local_start, end, local_end)
         for event in events:
    +        # Handle events in other timezones
    +        if event.dtstart.params.get('X-VOBJ-ORIGINAL-TZID') != None:
    +            event_tz = pytz.timezone(event.dtstart.params['X-VOBJ-ORIGINAL-TZID'][0])
    +            event.dtstart.value = event.dtstart.value.replace(tzinfo=event_tz)
    +            event.dtend.value = event.dtend.value.replace(tzinfo=event_tz)
    +            # Handle niave dates (&DST)
    +            if event.dtstart.value.timetuple().tm_isdst == 0:
    +                event.dtstart.value = event.dtstart.value + datetime.timedelta(hours = -1)
    +                event.dtend.value = event.dtend.value + datetime.timedelta(hours = -1)
             log.debug('checking %s - %s == %s for %s',
                       event.dtstart.value,
                       event.dtend.value,
    
  3. Ken Mankoff reporter

    Nope. Further testing reveals bug still exists. I'll keep working on it. If you aren't maintaining this anymore, do you mind if I fork it to github?

  4. Ken Mankoff reporter

    OK, a bit more time spent on this and a bit more testing and this time it appears to work. It probably will not work correctly for dates/times that span a DST change, or events set to occur in the strange missing or dual hours when the change happens, but it works for the dates and times that occur on either side of the changes.

    diff -r d2d5da0e2607 ical2org/app.py
    --- a/ical2org/app.py	Tue Jan 03 08:37:29 2012 -0500
    +++ b/ical2org/app.py	Sun Sep 30 08:26:33 2012 +0200
    @@ -121,7 +121,7 @@
                                  type='choice',
                                  choices=FORMATTER_FACTORIES.keys(),
                                  default='org',
    -                             help='Output format. One of %s. Defaults to "diary".' % FORMATTER_FACTORIES.keys(),
    +                             help='Output format. One of %s. Defaults to "org".' % FORMATTER_FACTORIES.keys(),
                                  )
         option_parser.add_option('--opt', '--formatter-option',
                                  action='callback',
    diff -r d2d5da0e2607 ical2org/filter.py
    --- a/ical2org/filter.py	Tue Jan 03 08:37:29 2012 -0500
    +++ b/ical2org/filter.py	Sun Sep 30 08:26:33 2012 +0200
    @@ -7,10 +7,12 @@
     """
     
     import datetime
    +import dateutil
     import logging
     import sys
     
     import vobject
    +import pytz
     
     from ical2org import tz
     
    @@ -23,6 +25,14 @@
         local_end = end.astimezone(tz.local)
         log.debug('filtering between %s (%s) and %s (%s)', start, local_start, end, local_end)
         for event in events:
    +        # Handle events in other timezones
    +        if event.dtstart.params.get('X-VOBJ-ORIGINAL-TZID') != None:
    +            event_tz = pytz.timezone(event.dtstart.params['X-VOBJ-ORIGINAL-TZID'][0])
    +            # http://stackoverflow.com/questions/12659108/converting-an-ambigious-time-to-a-correct-local-time-with-python
    +            event_tz_aware = event_tz.localize(event.dtstart.value)
    +            local_tz = dateutil.tz.tzlocal()          
    +            event.dtstart.value = event_tz_aware.astimezone(local_tz)
    +            event.dtend.value = event_tz_aware.astimezone(local_tz)
             log.debug('checking %s - %s == %s for %s',
                       event.dtstart.value,
                       event.dtend.value,
    
  5. Ken Mankoff reporter

    IMPROVED:

    diff -r d2d5da0e2607 ical2org/app.py
    --- a/ical2org/app.py	Tue Jan 03 08:37:29 2012 -0500
    +++ b/ical2org/app.py	Sun Sep 30 09:35:57 2012 +0200
    @@ -121,7 +121,7 @@
                                  type='choice',
                                  choices=FORMATTER_FACTORIES.keys(),
                                  default='org',
    -                             help='Output format. One of %s. Defaults to "diary".' % FORMATTER_FACTORIES.keys(),
    +                             help='Output format. One of %s. Defaults to "org".' % FORMATTER_FACTORIES.keys(),
                                  )
         option_parser.add_option('--opt', '--formatter-option',
                                  action='callback',
    diff -r d2d5da0e2607 ical2org/filter.py
    --- a/ical2org/filter.py	Tue Jan 03 08:37:29 2012 -0500
    +++ b/ical2org/filter.py	Sun Sep 30 09:35:57 2012 +0200
    @@ -7,10 +7,12 @@
     """
     
     import datetime
    +import dateutil
     import logging
     import sys
     
     import vobject
    +import pytz
     
     from ical2org import tz
     
    @@ -23,6 +25,15 @@
         local_end = end.astimezone(tz.local)
         log.debug('filtering between %s (%s) and %s (%s)', start, local_start, end, local_end)
         for event in events:
    +        # Handle events in other timezones
    +        if event.dtstart.params.get('X-VOBJ-ORIGINAL-TZID') != None:
    +            event_tz = pytz.timezone(event.dtstart.params['X-VOBJ-ORIGINAL-TZID'][0])
    +            # http://stackoverflow.com/questions/12659108/converting-an-ambigious-time-to-a-correct-local-time-with-python
    +            local_tz = dateutil.tz.tzlocal()          
    +            event_tz_aware = event_tz.localize(event.dtstart.value)
    +            event.dtstart.value = event_tz_aware.astimezone(local_tz)
    +            event_tz_aware = event_tz.localize(event.dtend.value)
    +            event.dtend.value = event_tz_aware.astimezone(local_tz)
             log.debug('checking %s - %s == %s for %s',
                       event.dtstart.value,
                       event.dtend.value,
    diff -r d2d5da0e2607 ical2org/org.py
    --- a/ical2org/org.py	Tue Jan 03 08:37:29 2012 -0500
    +++ b/ical2org/org.py	Sun Sep 30 09:35:57 2012 +0200
    @@ -86,9 +86,12 @@
                 log.debug('partial day event')
                 if day_span <= datetime.timedelta(1):
                     log.debug('single day')
    -                # Single day, partial day event
    -                time_range = '<%s-%s>' % (event_start.strftime('%Y-%m-%d %a %H:%M'),
    -                                          event_end.strftime('%H:%M'))
    +                if (event_end != event_start):
    +                    # Single day, partial day event, different start/end time (timespan)
    +                    time_range = '<%s-%s>' % (event_start.strftime('%Y-%m-%d %a %H:%M'),event_end.strftime('%H:%M'))
    +                else:
    +                    # Single day, same start/end time (timestamp)
    +                    time_range = '<%s>' % (event_start.strftime('%Y-%m-%d %a %H:%M'))
                 else:
                     # Multi-day, event at specific time
                     time_range = '<%s>--<%s>' % (event_start.strftime('%Y-%m-%d %a %H:%M'),
    
  6. Log in to comment