Building a list of dates between two dates My solution to this feels 'icky' and I've got calendar math falling out of my ears after working on similar problems for a week so I can't think straight about this.

Is there a better way to code this?

import datetime
from dateutil.relativedelta import relativedelta


def date_count(start, end, day_of_month=1):
    """
    Return a list of datetime.date objects that lie in-between start and end.

    The first element of the returned list will always be start and the last
    element in the returned list will always be:
        datetime.date(end.year, end.month, day_of_month)

    If start.day is equal to day_of_month the second element will be:
        start + 1 month

    If start.day is after day_of_month then the second element will be:
        the day_of_month in the next month

    If start.day is before day_of_month then the second element will be:
        datetime.date(start.year, start.month, day_of_month)


    &gt;&gt;&gt; start = datetime.date(2012, 1, 15)
    &gt;&gt;&gt; end = datetime.date(2012, 4, 1)
    &gt;&gt;&gt; date_count(start, end, day_of_month=1) #doctest: +NORMALIZE_WHITESPACE
    [datetime.date(2012, 1, 15), datetime.date(2012, 2, 1),
    datetime.date(2012, 3, 1), datetime.date(2012, 4, 1)]

    Notice that it's not a full month between the first two elements in the
    list.

    If you have a start day before day_of_month:
    &gt;&gt;&gt; start = datetime.date(2012, 1, 10)
    &gt;&gt;&gt; end = datetime.date(2012, 4, 1)
    &gt;&gt;&gt; date_count(start, end, day_of_month=15) #doctest: +NORMALIZE_WHITESPACE
    [datetime.date(2012, 1, 10), datetime.date(2012, 1, 15),
    datetime.date(2012, 2, 15), datetime.date(2012, 3, 15),
    datetime.date(2012, 4, 15)]

    Notice that it's not a full month between the first two elements in the
    list and that the last day is rounded to
    datetime.date(end.year, end.month, day_of_month)
    """
    last_element = datetime.date(end.year, end.month, day_of_month)

    if start.day == day_of_month:
        second_element = start + relativedelta(start, months=+1)
    elif start.day &gt; day_of_month:
        _ = datetime.date(start.year, start.month, day_of_month)
        second_element = _ + relativedelta(_, months=+1)
    else:
        second_element = datetime.date(start.year, start.month, day_of_month)

    dates = [start, second_element]

    if last_element &lt;= second_element:
        return dates

    while dates[-1] &lt; last_element:
        next_date = dates[-1] + relativedelta(dates[-1], months=+1)
        next_date = datetime.date(next_date.year, next_date.month, day_of_month)
        dates.append(next_date)
    dates.pop()

    dates.append(last_element)

    return dates
How about...

def date_count(start, end, day_of_month=1):
    dates = [start]
    next_date = start.replace(day=day_of_month)
    if day_of_month &gt; start.day:
        dates.append(next_date)
    while next_date &lt; end.replace(day=day_of_month):
        next_date += relativedelta(next_date, months=+1)
        dates.append(next_date)
    return dates


And by the way it seems like a nice opportunity to use yield, if you wanted to.

def date_count2(start, end, day_of_month=1):
    yield start
    next_date = start.replace(day=day_of_month)
    if day_of_month &gt; start.day:
        yield next_date
    while next_date &lt; end.replace(day=day_of_month):
        next_date += relativedelta(next_date, months=+1)
        yield next_date


Another possibility - discard the first value if it is earlier than the start date:

def date_count(start, end, day_of_month=1):
    dates = [start.replace(day=day_of_month)]
    while dates[-1] &lt; end.replace(day=day_of_month):
        dates.append(dates[-1] + relativedelta(dates[-1], months=+1))
    if dates[0] &gt; start:
        return [start] + dates
    else:
        return [start] + dates[1:]


Or use a list comprehension to iterate over the number of months between start and end.

def date_count(start, end, day_of_month=1):
    round_start = start.replace(day=day_of_month)
    gap = end.year * 12 + end.month - start.year * 12 - start.month + 1
    return [start] + [round_start + relativedelta(round_start, months=i) 
                      for i in range(day_of_month &lt;= start.day, gap)]


Finally, I don't know dateutil but it seems you can use rrule:

from dateutil import rrule
def date_count(start, end, day_of_month=1):
    yield start
    for date in rrule(MONTHLY, 
                      dtstart=start.replace(day=day_of_month),
                      until=end.replace(day=day_of_month)):
        if date &gt; start:
            yield date