# Python - get tuple of the first and last days of the last N months

I am trying to get a tuple of date (or datetime) objects over the last N months.

My thought was to use the dateutil package with something like this:

def last_n_months(n=12, ending=None):
"""Return a list of tuples of the first/last day of the month
for the last N months
"""
from datetime import date
from dateutil.rrule import rrule, MONTHLY
from dateutil.relativedelta import relativedelta

if not ending:
ending = date.today()

# set the ending date to the last day of the month
ending = ending + relativedelta(months=+1, days=-ending.day)

# starting is the first day of the month N months ago
starting = ending - relativedelta(months=n, day=1)

months = list(rrule(MONTHLY, bymonthday=(1, -1), dtstart=starting,
until=ending))

# we return pairs of dates like this: (1st day of month, last day of month)
months = zip(months[::2], months[1::2])

return months

Example usage:

>>> from datetime import date, timedelta

# get last two months as a degenerate example
>>> l2n = last_n_months(2, ending=date(2012, 01, 01))
>>> map(lambda x: [x[0].year, x[0].month, x[0].day], l2n)
[[2011, 11, 1], [2011, 12, 1], [2012, 1, 1]]
>>> map(lambda x: [x[1].year, x[1].month, x[1].day], l2n)
[[2011, 11, 30], [2011, 12, 31], [2012, 1, 31]]

>>> l24n = last_n_months(24, ending=date(2012,03,16))
>>> len(l24n) # inclusive of current month
25

# every tuple starts with the first day of the month
>>> all(x[0].day == 1 for x in l24n)
True

# every tuple ends with the last day of the month
>>> all((x[1] +timedelta(days=1)).month != x[1].month for x in l24n)
True

# every tuple is the same month
>>> all(x[0].month == x[1].month for x in l24n)
True

I am posting it here to see if anyone has better solution than this (and perhaps see if this yields some off-by-one sort of error that I haven't thought of). Is there a simpler or faster solution than this?

-

## migrated from stackoverflow.comApr 18 '13 at 16:46

This question came from our site for professional and enthusiast programmers.

def last_n_months(n=12, ending=None):
"""Return a list of tuples of the first/last day of the month
for the last N months
"""
from datetime import date
from dateutil.rrule import rrule, MONTHLY
from dateutil.relativedelta import relativedelta

It is better to import outside of the function. Typically imports go at the top of the file.

if not ending:

You should check for none like: if ending is not None: just to be explicit about what you are checking for.

ending = date.today()

# set the ending date to the last day of the month
ending = ending + relativedelta(months=+1, days=-ending.day)

Modifying ending rubs me the wrong way.

# starting is the first day of the month N months ago
starting = ending - relativedelta(months=n, day=1)

months = list(rrule(MONTHLY, bymonthday=(1, -1), dtstart=starting,
until=ending))

# we return pairs of dates like this: (1st day of month, last day of month)
months = zip(months[::2], months[1::2])

return months

You can combine these two lines

-
Thanks Winston, much appreciated. Quick question with respect to the imports, is the preference to import outside the function a preference or functional? From a PEP? Always preferable where there are no circular dependencies? Or is there a concern about cluttering the namespace? Just curious - I'd be very grateful if anyone were to post a link to a discussion. –  Brian M. Hunt Apr 18 '13 at 20:26
@BrianM.Hunt, PEP 8 says to put all imports at the top. See here for some discussion of it: stackoverflow.com/questions/477096/python-import-coding-style –  Winston Ewert Apr 18 '13 at 21:17
Thanks for the discussion thread. A quote I thought interesting in one of the upvoted answers "As you can see, it can be more efficient to import the module in the function" –  Brian M. Hunt Apr 18 '13 at 22:08
@BrianM.Hunt, yes but that only applies if you are calling the function a lot. In your case it doesn't, because you only use any of those imports a few times. –  Winston Ewert Apr 18 '13 at 22:45