def date_range(start_date, end_date=None, num=None, delta=None):
if not delta:<EOL><INDENT>return []<EOL><DEDENT>if end_date and start_date > end_date:<EOL><INDENT>raise Exception("<STR_LIT>")<EOL><DEDENT>if end_date and num:<EOL><INDENT>raise Exception("<STR_LIT>")<EOL><DEDENT>if not end_date and not num:<EOL><INDENT>end_date = timezone.utcnow()<EOL><DEDENT>delta_iscron = False<EOL>tz = start_date.tzinfo<EOL>if isinstance(delta, six.string_types):<EOL><INDENT>delta_iscron = True<EOL>start_date = timezone.make_naive(start_date, tz)<EOL>cron = croniter(delta, start_date)<EOL><DEDENT>elif isinstance(delta, timedelta):<EOL><INDENT>delta = abs(delta)<EOL><DEDENT>dates = []<EOL>if end_date:<EOL><INDENT>if timezone.is_naive(start_date):<EOL><INDENT>end_date = timezone.make_naive(end_date, tz)<EOL><DEDENT>while start_date <= end_date:<EOL><INDENT>if timezone.is_naive(start_date):<EOL><INDENT>dates.append(timezone.make_aware(start_date, tz))<EOL><DEDENT>else:<EOL><INDENT>dates.append(start_date)<EOL><DEDENT>if delta_iscron:<EOL><INDENT>start_date = cron.get_next(datetime)<EOL><DEDENT>else:<EOL><INDENT>start_date += delta<EOL><DEDENT><DEDENT><DEDENT>else:<EOL><INDENT>for _ in range(abs(num)):<EOL><INDENT>if timezone.is_naive(start_date):<EOL><INDENT>dates.append(timezone.make_aware(start_date, tz))<EOL><DEDENT>else:<EOL><INDENT>dates.append(start_date)<EOL><DEDENT>if delta_iscron:<EOL><INDENT>if num > <NUM_LIT:0>:<EOL><INDENT>start_date = cron.get_next(datetime)<EOL><DEDENT>else:<EOL><INDENT>start_date = cron.get_prev(datetime)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>if num > <NUM_LIT:0>:<EOL><INDENT>start_date += delta<EOL><DEDENT>else:<EOL><INDENT>start_date -= delta<EOL><DEDENT><DEDENT><DEDENT><DEDENT>return sorted(dates)<EOL>
Get a set of dates as a list based on a start, end and delta, delta
can be something that can be added to `datetime.datetime`
or a cron expression as a `str`

:Example::

    date_range(datetime(2016, 1, 1), datetime(2016, 1, 3), delta=timedelta(1))
        [datetime.datetime(2016, 1, 1, 0, 0), datetime.datetime(2016, 1, 2, 0, 0),
        datetime.datetime(2016, 1, 3, 0, 0)]
    date_range(datetime(2016, 1, 1), datetime(2016, 1, 3), delta='0 0 * * *')
        [datetime.datetime(2016, 1, 1, 0, 0), datetime.datetime(2016, 1, 2, 0, 0),
        datetime.datetime(2016, 1, 3, 0, 0)]
    date_range(datetime(2016, 1, 1), datetime(2016, 3, 3), delta="0 0 0 * *")
        [datetime.datetime(2016, 1, 1, 0, 0), datetime.datetime(2016, 2, 1, 0, 0),
        datetime.datetime(2016, 3, 1, 0, 0)]

:param start_date: anchor date to start the series from
:type start_date: datetime.datetime
:param end_date: right boundary for the date range
:type end_date: datetime.datetime
:param num: alternatively to end_date, you can specify the number of
    number of entries you want in the range. This number can be negative,
    output will always be sorted regardless
:type num: int