# Buggy code
def expand(vevent, href=""):
    """
    Constructs a list of start and end dates for all recurring instances of the
    event defined in vevent.

    It considers RRULE as well as RDATE and EXDATE properties. In case of
    unsupported recursion rules an UnsupportedRecurrence exception is thrown.
    If the timezone defined in vevent is not understood by icalendar,
    default_tz is used.

    :param vevent: vevent to be expanded
    :type vevent: icalendar.cal.Event
    :param href: the href of the vevent, used for more informative logging and
                 nothing else
    :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

    events_tz = getattr(vevent["DTSTART"].dt, "tzinfo", None)
    allday = not isinstance(vevent["DTSTART"].dt, datetime)

    def sanitize_datetime(date):
        if allday and isinstance(date, datetime):
            date = date.date()
        if events_tz is not None:
            date = events_tz.localize(date)
        return date

    rrule_param = vevent.get("RRULE")
    if rrule_param is not None:
        vevent = sanitize_rrule(vevent)

        # dst causes problem while expanding the rrule, therefore we transform
        # everything to naive datetime objects and tranform back after
        # expanding
        # See https://github.com/dateutil/dateutil/issues/102
        dtstart = vevent["DTSTART"].dt
        if events_tz:
            dtstart = dtstart.replace(tzinfo=None)

        rrule = dateutil.rrule.rrulestr(rrule_param.to_ical().decode(), dtstart=dtstart)

        if rrule._until is None:
            # rrule really doesn't like to calculate all recurrences until
            # eternity, so we only do it until 2037, because a) I'm not sure
            # if python can deal with larger datetime values yet and b) pytz
            # doesn't know any larger transition times
            rrule._until = datetime(2037, 12, 31)
        elif getattr(rrule._until, "tzinfo", None):
            rrule._until = rrule._until.astimezone(events_tz).replace(tzinfo=None)

        rrule = map(sanitize_datetime, rrule)

        logger.debug(
            "calculating recurrence dates for {0}, this might take some time.".format(
                href
            )
        )

        # RRULE and RDATE may specify the same date twice, it is recommended by
        # the RFC to consider this as only one instance
        dtstartl = set(rrule)
        if not dtstartl:
            raise UnsupportedRecurrence()
    else:
        dtstartl = {vevent["DTSTART"].dt}

    def get_dates(vevent, key):
        dates = vevent.get(key)
        if dates is None:
            return
        if not isinstance(dates, list):
            dates = [dates]

        dates = (leaf.dt for tree in dates for leaf in tree.dts)
        dates = localize_strip_tz(dates, events_tz)
        return map(sanitize_datetime, dates)

    # include explicitly specified recursion dates
    dtstartl.update(get_dates(vevent, "RDATE") or ())

    # remove excluded dates
    for date in get_dates(vevent, "EXDATE") or ():
        dtstartl.remove(date)

    dtstartend = [(start, start + duration) for start in dtstartl]
    # not necessary, but I prefer deterministic output
    dtstartend.sort()
    return dtstartend


# Traceback (KeyError)
Unknown exception happened.
Traceback (most recent call last):
File "/usr/lib/python3.5/site-packages/khal/khalendar/khalendar.py", line 286, in _update_vevent
update(event.raw, href=href, etag=etag, calendar=calendar)
File "/usr/lib/python3.5/site-packages/khal/khalendar/backend.py", line 260, in update
self._update_impl(vevent, href, calendar)
File "/usr/lib/python3.5/site-packages/khal/khalendar/backend.py", line 349, in _update_impl
dtstartend = aux.expand(vevent, href)
File "/usr/lib/python3.5/site-packages/khal/khalendar/aux.py", line 106, in expand
dtstartl.remove(date)
KeyError: datetime.datetime(2011, 9, 24, 15, 50, tzinfo=<DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST>)
warning: Skipping home/00D6A925-90F7-4663-AE13-E8BD1655EF77.ics: datetime.datetime(2011, 9, 24, 15, 50, tzinfo=<DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST>)
warning: This event will not be available in khal.

# Fixed code
def expand(vevent, href=""):
    """
    Constructs a list of start and end dates for all recurring instances of the
    event defined in vevent.

    It considers RRULE as well as RDATE and EXDATE properties. In case of
    unsupported recursion rules an UnsupportedRecurrence exception is thrown.
    If the timezone defined in vevent is not understood by icalendar,
    default_tz is used.

    :param vevent: vevent to be expanded
    :type vevent: icalendar.cal.Event
    :param href: the href of the vevent, used for more informative logging and
                 nothing else
    :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

    events_tz = getattr(vevent["DTSTART"].dt, "tzinfo", None)
    allday = not isinstance(vevent["DTSTART"].dt, datetime)

    def sanitize_datetime(date):
        if allday and isinstance(date, datetime):
            date = date.date()
        if events_tz is not None:
            date = events_tz.localize(date)
        return date

    rrule_param = vevent.get("RRULE")
    if rrule_param is not None:
        vevent = sanitize_rrule(vevent)

        # dst causes problem while expanding the rrule, therefore we transform
        # everything to naive datetime objects and tranform back after
        # expanding
        # See https://github.com/dateutil/dateutil/issues/102
        dtstart = vevent["DTSTART"].dt
        if events_tz:
            dtstart = dtstart.replace(tzinfo=None)

        rrule = dateutil.rrule.rrulestr(rrule_param.to_ical().decode(), dtstart=dtstart)

        if rrule._until is None:
            # rrule really doesn't like to calculate all recurrences until
            # eternity, so we only do it until 2037, because a) I'm not sure
            # if python can deal with larger datetime values yet and b) pytz
            # doesn't know any larger transition times
            rrule._until = datetime(2037, 12, 31)
        elif getattr(rrule._until, "tzinfo", None):
            rrule._until = rrule._until.astimezone(events_tz).replace(tzinfo=None)

        rrule = map(sanitize_datetime, rrule)

        logger.debug(
            "calculating recurrence dates for {0}, this might take some time.".format(
                href
            )
        )

        # RRULE and RDATE may specify the same date twice, it is recommended by
        # the RFC to consider this as only one instance
        dtstartl = set(rrule)
        if not dtstartl:
            raise UnsupportedRecurrence()
    else:
        dtstartl = {vevent["DTSTART"].dt}

    def get_dates(vevent, key):
        dates = vevent.get(key)
        if dates is None:
            return
        if not isinstance(dates, list):
            dates = [dates]

        dates = (leaf.dt for tree in dates for leaf in tree.dts)
        dates = localize_strip_tz(dates, events_tz)
        return map(sanitize_datetime, dates)

    # include explicitly specified recursion dates
    dtstartl.update(get_dates(vevent, "RDATE") or ())

    # remove excluded dates
    for date in get_dates(vevent, "EXDATE") or ():
        try:
            dtstartl.remove(date)
        except KeyError:
            logger.warn(
                "In event {}, excluded instance starting at {} not found, "
                "event might be invalid.".format(href, date)
            )

    dtstartend = [(start, start + duration) for start in dtstartl]
    # not necessary, but I prefer deterministic output
    dtstartend.sort()
    return dtstartend