Parse RRULE icalendar entries I'm using the icalendar module to parse (google) calendar entries and the dateutil.rrule module to deal with recurring events.

Right now, I'm using the rrulestr class to parse (part of) the RRULE data and rruleset to add EXDATE entries:

from icalendar import Event
from dateutil.rrule import rrulestr, rruleset
from datetime import datetime, timedelta
from pytz import UTC    
from pprint import pprint

# example google calendar entry
event_text = """BEGIN:VEVENT
SUMMARY:Test Event
DTSTART;TZID=Europe/Paris:20160806T100000
DTEND;TZID=Europe/Paris:20160806T120000
RRULE:FREQ=WEEKLY;UNTIL=20160910T000000Z
EXDATE;TZID=Europe/Paris:20160813T100000
EXDATE;TZID=Europe/Paris:20160820T100000
END:VEVENT"""

event = Event.from_ical(event_text)

rules_text = '\n'.join([line for line in event.content_lines()
                        if line.startswith('RRULE')])

rules = rruleset()

first_rule = rrulestr(rules_text,
                      dtstart=event.get('dtstart').dt)

# in some entries, tzinfo is missing
if first_rule._until and first_rule._until.tzinfo is None:
    first_rule._until = first_rule._until.replace(tzinfo=UTC)

rules.rrule(first_rule)

exdates = event.get('exdate')

if not isinstance(exdates, list):  # apparently this isn't a list when
    exdates = [exdates]            # there is only one EXDATE
for exdate in exdates:
    try:
        rules.exdate(exdate.dts[0].dt)
    except AttributeError:  # sometimes there is a None entry here
        pass

now = datetime.now(tz=UTC)
in_2_months = now + timedelta(days=60)
pprint(rules.between(now, in_2_months))


Apparently this works, but I'm wondering if there is a more compact and cleaner way of doing this?

I especially don't like the fact that the RRULE string is parsed twice.

Could the absence of tzinfo be handled differently?

Something else?
... a more compact and cleaner way of doing this? I especially don't like the fact that the RRULE string is parsed twice.


If you're going to use the rrule module's public API, then yes, your approach is the best way of doing this.