# Buggy code
def expand(vevent, default_tz, href=""):
    """

    :param vevent: vevent to be expanded
    :type vevent: icalendar.cal.Event
    :param default_tz: the default timezone used when we (icalendar)
                       don't understand the embedded timezone
    :type default_tz: pytz.timezone
    :param href: the href of the vevent, used for more informative logging
    :type href: str
    :returns: list of start and end (date)times of the expanded event
    :rtyped list(tuple(datetime, datetime))
    """
    # we do this now and than never care about the "real" end time again
    if "DURATION" in vevent:
        duration = vevent["DURATION"].dt
    else:
        duration = vevent["DTEND"].dt - vevent["DTSTART"].dt

    # dateutil.rrule converts everything to datetime
    allday = True if not isinstance(vevent["DTSTART"].dt, datetime) else False

    # icalendar did not understand the defined timezone
    if (
        not allday
        and "TZID" in vevent["DTSTART"].params
        and vevent["DTSTART"].dt.tzinfo is None
    ):
        vevent["DTSTART"].dt = default_tz.localize(vevent["DTSTART"].dt)

    if "RRULE" not in vevent.keys():
        return [(vevent["DTSTART"].dt, vevent["DTSTART"].dt + duration)]

    events_tz = None
    if (
        hasattr(vevent["DTSTART"].dt, "tzinfo")
        and vevent["DTSTART"].dt.tzinfo is not None
    ):
        events_tz = vevent["DTSTART"].dt.tzinfo
        vevent["DTSTART"].dt = vevent["DTSTART"].dt.astimezone(pytz.UTC)

    rrulestr = vevent["RRULE"].to_ical()
    rrule = dateutil.rrule.rrulestr(rrulestr, dtstart=vevent["DTSTART"].dt)
    if not set(["UNTIL", "COUNT"]).intersection(vevent["RRULE"].keys()):
        # rrule really doesn't like to calculate all recurrences until
        # eternity, so we only do it 15years into the future
        rrule._until = vevent["DTSTART"].dt + timedelta(days=15 * 365)
    if (
        rrule._until is not None
        and rrule._until.tzinfo is None
        and vevent["DTSTART"].dt.tzinfo is not None
    ):
        rrule._until = vevent["DTSTART"].dt.tzinfo.localize(rrule._until)
    logging.debug(
        "calculating recurrence dates for {0}, this might take some time.".format(href)
    )
    dtstartl = list(rrule)

    if len(dtstartl) == 0:
        raise UnsupportedRecursion

    if events_tz is not None:
        dtstartl = [start.astimezone(events_tz) for start in dtstartl]
    elif allday:
        dtstartl = [start.date() for start in dtstartl]

    dtstartend = [(start, start + duration) for start in dtstartl]
    return dtstartend


# Traceback (AttributeError)
jason@valis:~> khal --sync
Traceback (most recent call last):
File "/usr/bin/khal", line 5, in <module>
pkg_resources.run_script('khal==0.2.0.dev-62-g2c8baea', 'khal')
File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 499, in run_script
self.require(requires)[0].run_script(script_name, ns)
File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 1255, in run_script
execfile(script_filename, namespace, namespace)
File "/usr/lib/python2.7/site-packages/khal-0.2.0.dev_62_g2c8baea-py2.7.egg/EGG-INFO/scripts/khal", line 74, in <module>
default_tz=conf.default.default_timezone
File "/usr/lib/python2.7/site-packages/khal-0.2.0.dev_62_g2c8baea-py2.7.egg/khal/khalendar/khalendar.py", line 82, in __init__
self.db_update()
File "/usr/lib/python2.7/site-packages/khal-0.2.0.dev_62_g2c8baea-py2.7.egg/khal/khalendar/khalendar.py", line 160, in db_update
self.update_vevent(href)
File "/usr/lib/python2.7/site-packages/khal-0.2.0.dev_62_g2c8baea-py2.7.egg/khal/khalendar/khalendar.py", line 166, in update_vevent
ignore_invalid_items=True)
File "/usr/lib/python2.7/site-packages/khal-0.2.0.dev_62_g2c8baea-py2.7.egg/khal/khalendar/backend.py", line 279, in update
href)
File "/usr/lib/python2.7/site-packages/khal-0.2.0.dev_62_g2c8baea-py2.7.egg/khal/khalendar/datetimehelper.py", line 35, in expand
vevent['DTSTART'].dt.tzinfo is None):
AttributeError: 'datetime.date' object has no attribute 'tzinfo'

# Fixed code
def expand(vevent, default_tz, href=""):
    """

    :param vevent: vevent to be expanded
    :type vevent: icalendar.cal.Event
    :param default_tz: the default timezone used when we (icalendar)
                       don't understand the embedded timezone
    :type default_tz: pytz.timezone
    :param href: the href of the vevent, used for more informative logging
    :type href: str
    :returns: list of start and end (date)times of the expanded event
    :rtyped list(tuple(datetime, datetime))
    """
    # we do this now and than never care about the "real" end time again
    if "DURATION" in vevent:
        duration = vevent["DURATION"].dt
    else:
        duration = vevent["DTEND"].dt - vevent["DTSTART"].dt

    # dateutil.rrule converts everything to datetime
    allday = True if not isinstance(vevent["DTSTART"].dt, datetime) else False

    # icalendar did not understand the defined timezone
    if (
        not allday
        and "TZID" in vevent["DTSTART"].params
        and vevent["DTSTART"].dt.tzinfo is None
    ):
        vevent["DTSTART"].dt = default_tz.localize(vevent["DTSTART"].dt)

    if "RRULE" not in vevent.keys():
        return [(vevent["DTSTART"].dt, vevent["DTSTART"].dt + duration)]

    events_tz = None
    if getattr(vevent["DTSTART"].dt, "tzinfo", False):
        events_tz = vevent["DTSTART"].dt.tzinfo
        vevent["DTSTART"].dt = vevent["DTSTART"].dt.astimezone(pytz.UTC)

    rrulestr = vevent["RRULE"].to_ical()
    rrule = dateutil.rrule.rrulestr(rrulestr, dtstart=vevent["DTSTART"].dt)
    if not set(["UNTIL", "COUNT"]).intersection(vevent["RRULE"].keys()):
        # rrule really doesn't like to calculate all recurrences until
        # eternity, so we only do it 15years into the future
        rrule._until = vevent["DTSTART"].dt + timedelta(days=15 * 365)
    if (not getattr(rrule._until, "tzinfo", True)) and (
        not getattr(vevent["DTSTART"].dt, "tzinfo", True)
    ):
        rrule._until = vevent["DTSTART"].dt.tzinfo.localize(rrule._until)
    logging.debug(
        "calculating recurrence dates for {0}, this might take some time.".format(href)
    )
    dtstartl = list(rrule)

    if len(dtstartl) == 0:
        raise UnsupportedRecursion

    if events_tz is not None:
        dtstartl = [start.astimezone(events_tz) for start in dtstartl]
    elif allday:
        dtstartl = [start.date() for start in dtstartl]

    dtstartend = [(start, start + duration) for start in dtstartl]
    return dtstartend