def cumulative_time_on_drug(
        drug_events_df: DataFrame,
        query_times_df: DataFrame,
        event_lasts_for_timedelta: datetime.timedelta = None,
        event_lasts_for_quantity: float = None,
        event_lasts_for_units: str = None,
        patient_colname: str = DEFAULT_PATIENT_COLNAME,
        event_datetime_colname: str = DEFAULT_DRUG_EVENT_DATETIME_COLNAME,
        start_colname: str = DEFAULT_START_DATETIME_COLNAME,
        when_colname: str = DEFAULT_QUERY_DATETIME_COLNAME,
        include_timedelta_in_output: bool = False,
        debug: bool = False) \
        -> DataFrame:
    """

    Args:
        drug_events_df:
            pandas :class:`DataFrame` containing the event data, with columns
            named according to ``patient_colname``, ``event_datetime_colname``
        event_lasts_for_timedelta:
            when an event occurs, how long is it assumed to last for? For
            example, if a prescription of lithium occurs on 2001-01-01, how
            long is the patient presumed to be taking lithium as a consequence
            (e.g. 1 day? 28 days? 6 months?)
        event_lasts_for_quantity:
            as an alternative to ``event_lasts_for_timedelta``, particularly if
            you are calling from R to Python via ``reticulate`` (which doesn't
            convert R ``as.difftime()`` to Python ``datetime.timedelta``), you
            can specify ``event_lasts_for_quantity``, a number and
            ``event_lasts_for_units`` (q.v.).
        event_lasts_for_units:
            specify the units for ``event_lasts_for_quantity`` (q.v.), if used;
            e.g. ``"days"``. The string value must be the name of an argument
            to the Python ``datetime.timedelta`` constructor.
        query_times_df:
            times to query for, with columns named according to
            ``patient_colname``, ``start_colname``, and ``when_colname``
        patient_colname:
            name of the column in ``drug_events_df`` and ``query_time_df``
            containing the patient ID
        event_datetime_colname:
            name of the column in ``drug_events_df`` containing the date/time
            of each event
        start_colname:
            name of the column in ``query_time_df`` containing the date/time
            representing the overall start time for the relevant patient (from
            which cumulative times are calculated)
        when_colname:
            name of the column in ``query_time_df`` containing date/time
            values at which to query
        include_timedelta_in_output:
            include ``datetime.timedelta`` values in the output? The default is
            ``False`` as this isn't supported by R/``reticulate``.
        debug:
            print debugging information to the log?

    Returns:
        :class:`DataFrame` with the requested data

    """
    if event_lasts_for_timedelta is None:
        assert event_lasts_for_quantity and event_lasts_for_units
        timedelta_dict = {event_lasts_for_units: event_lasts_for_quantity}
        event_lasts_for_timedelta = datetime.timedelta(**timedelta_dict)
    if debug:
        log.critical("drug_events_df:\n{!r}", drug_events_df)
        log.critical("event_lasts_for:\n{!r}", event_lasts_for_timedelta)
        log.critical("query_times_df:\n{!r}", query_times_df)
    timelines = drug_timelines(
        drug_events_df=drug_events_df,
        event_lasts_for=event_lasts_for_timedelta,
        patient_colname=patient_colname,
        event_datetime_colname=event_datetime_colname,
    )
    query_nrow = len(query_times_df)
    ct_coldefs = [  # column definitions:
        (RCN_PATIENT_ID, DTYPE_STRING),
        (RCN_START, DTYPE_DATETIME),
        (RCN_TIME, DTYPE_DATETIME),
        (RCN_BEFORE_DAYS, DTYPE_FLOAT),
        (RCN_DURING_DAYS, DTYPE_FLOAT),
        (RCN_AFTER_DAYS, DTYPE_FLOAT),
    ]
    if include_timedelta_in_output:
        ct_coldefs.extend([
            (RCN_BEFORE_TIMEDELTA, DTYPE_TIMEDELTA),
            (RCN_DURING_TIMEDELTA, DTYPE_TIMEDELTA),
            (RCN_AFTER_TIMEDELTA, DTYPE_TIMEDELTA),
        ])
    ct_arr = array([None] * query_nrow, dtype=ct_coldefs)
    # log.debug("ct_arr:\n{!r}", ct_arr)
    cumulative_times = DataFrame(ct_arr, index=list(range(query_nrow)))
    # log.debug("cumulative_times:\n{!r}", cumulative_times)
    # So we can use the fast "iat" function.
    sourcecolnum_pt = query_times_df.columns.get_loc(patient_colname)
    sourcecolnum_start = query_times_df.columns.get_loc(start_colname)
    sourcecolnum_when = query_times_df.columns.get_loc(when_colname)
    dest_colnum_pt = cumulative_times.columns.get_loc(RCN_PATIENT_ID)
    dest_colnum_start = cumulative_times.columns.get_loc(RCN_START)
    dest_colnum_t = cumulative_times.columns.get_loc(RCN_TIME)
    dest_colnum_before_days = cumulative_times.columns.get_loc(RCN_BEFORE_DAYS)
    dest_colnum_during_days = cumulative_times.columns.get_loc(RCN_DURING_DAYS)
    dest_colnum_after_days = cumulative_times.columns.get_loc(RCN_AFTER_DAYS)
    if include_timedelta_in_output:
        dest_colnum_before_dt = cumulative_times.columns.get_loc(RCN_BEFORE_TIMEDELTA)  # noqa
        dest_colnum_during_dt = cumulative_times.columns.get_loc(RCN_DURING_TIMEDELTA)  # noqa
        dest_colnum_after_dt = cumulative_times.columns.get_loc(RCN_AFTER_TIMEDELTA)  # noqa
    else:
        # for type checker
        dest_colnum_before_dt = 0
        dest_colnum_during_dt = 0
        dest_colnum_after_dt = 0
    for rowidx in range(query_nrow):
        patient_id = query_times_df.iat[rowidx, sourcecolnum_pt]
        start = query_times_df.iat[rowidx, sourcecolnum_start]
        when = query_times_df.iat[rowidx, sourcecolnum_when]
        ivlist = timelines[patient_id]
        # log.critical("ivlist: {!r}", ivlist)
        before, during, after = ivlist.cumulative_before_during_after(start,
                                                                      when)
        # log.critical(
        #     "{!r}.cumulative_before_during_after(start={!r}, when={!r}) "
        #     "-> {!r}, {!r}, {!r}",
        #     ivlist, start, when,
        #     before, during, after
        # )
        cumulative_times.iat[rowidx, dest_colnum_pt] = patient_id
        cumulative_times.iat[rowidx, dest_colnum_start] = start
        cumulative_times.iat[rowidx, dest_colnum_t] = when
        cumulative_times.iat[rowidx, dest_colnum_before_days] = before.days
        cumulative_times.iat[rowidx, dest_colnum_during_days] = during.days
        cumulative_times.iat[rowidx, dest_colnum_after_days] = after.days
        if include_timedelta_in_output:
            cumulative_times.iat[rowidx, dest_colnum_before_dt] = before
            cumulative_times.iat[rowidx, dest_colnum_during_dt] = during
            cumulative_times.iat[rowidx, dest_colnum_after_dt] = after
    return cumulative_times