Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

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?

share|improve this question

migrated from stackoverflow.com Apr 18 '13 at 16:46

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

1 Answer 1

up vote 2 down vote accepted
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

share|improve this answer
    
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

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.