views:

264

answers:

5

Hi,

Let's say i have 2 strings 'Jan-2010' and 'Mar-2010' and i want to parse it such that it returns 2 datetime objects: 1-Jan-2010 and 31-Mar-2010 (i.e. the last day).

What would be the best strategy in python? Should i just split the string into tokens or use regular expressions and then use the calendar functions to get say the last day of the month for 'Mar-2010' (getting the first day is trivial, its always 1 in this case unless i wanted the first working day of the month).

Any suggestions? Thanks in advance.

A: 

Create a range of dates.

mcandre
Not sure how a date range would help here, considering they don't want all of the intermediate dates, just the first day of the first month, and the last day of the second month.
Amber
+2  A: 
from datetime import datetime, timedelta

def first_day(some_date):
    return some_date.replace(day=1, hour=0, minute=0, second=0, microsecond=0)

def next_month(some_date):
    return first_day(first_day(some_date) + timedelta(days=31))

def last_day(some_date):
    return next_month(some_date) - timedelta(days=1)

# testing:

months = [('Jan-2010', 'Mar-2010'), # your example
          ('Apr-2009', 'Apr-2009'), # same month, 30 days
          ('Jan-2008', 'Dec-2008'), # whole year
          ('Jan-2007', 'Feb-2007')] # february involved

for date1, date2 in months:
    print first_day(datetime.strptime(date1, '%b-%Y')),
    print '-', 
    print last_day(datetime.strptime(date2, '%b-%Y'))

That prints:

2010-01-01 00:00:00 - 2010-03-31 00:00:00
2009-04-01 00:00:00 - 2009-04-30 00:00:00
2008-01-01 00:00:00 - 2008-12-31 00:00:00
2007-01-01 00:00:00 - 2007-02-28 00:00:00
nosklo
+3  A: 

strptime does the string parsing into dates on your behalf:

def firstofmonth(MmmYyyy):
  return datetime.datetime.strptime(MmmYyyy, '%b-%Y').date()

much better than messing around with tokenization, regexp, &c!-).

To get the date of the last day of the month, you can indeed use the calendar module:

def lastofmonth(MmmYyyy):
  first = firstofmonth(MmmYyyy)
  _, lastday = calendar.monthrange(first.year, first.month)
  return datetime.date(first.year, first.month, lastday)

You could ALMOST do it neatly with datetime alone, e.g., an ALMOST working approach:

def lastofmonth(MmmYyyy):
  first = firstofmonth(MmmYyyy)
  return first.replace(month=first.month+1, day=1
             ) - datetime.timedelta(days=1)

but, alas!, this breaks for December, and the code needed to specialcase December makes the overall approach goofier than calendar affords;-).

Alex Martelli
@Alex: To make it work across december one has to add to the days (not to the months) since there's no month relative timedelta.
nosklo
@nosko, yep, I've seen your nifty trick (adding 31 days then going back to the 1st of that month) though I don't like tossing datetimes around when dates are all that's needed and .date() does the extraction of date from datetime just fine;-)
Alex Martelli
@Alex: Yes but... I don't see how what you said is related to what we were talking before.
nosklo
@nosklo == uh...? I didn't use a month-relative timedelta, obviously, so I thought you were talking about the nifty trick in your answer; so I explained why your answer, while clever!, is not to my liking (using datetimes with fake times when just using dates as I did is so much more straightforward).
Alex Martelli
A: 

Riffing on Alex Martelli's:

import datetime
def lastofmonthHelper(MmmYyyy): # Takes a date
  return MmmYyyy.replace(year=MmmYyyy.year+(MmmYyyy.month==12), month=MmmYyyy.month%12 + 1, day=1) - datetime.timedelta(days=1)

>>> for month in range(1,13):
...     t = datetime.date(2009,month,1)
...     print t, lastofmonthHelper(t)
...
2009-01-01 2009-01-31
2009-02-01 2009-02-28
2009-03-01 2009-03-31
2009-04-01 2009-04-30
2009-05-01 2009-05-31
2009-06-01 2009-06-30
2009-07-01 2009-07-31
2009-08-01 2009-08-31
2009-09-01 2009-09-30
2009-10-01 2009-10-31
2009-11-01 2009-11-30
2009-12-01 2009-12-31

You don't have to use the first day of the month, BTW. I would have put this in a comment but we all know how the formatting would have turned out. Feel free to upvote Alex.

If you call with the result of a firstofmonth() call, you get the desired result:

>>> lastofmonthHelper(firstofmonth('Apr-2009'))
datetime.date(2009, 4, 30)
hughdbrown
+2  A: 

I highly recommend using the python timeseries module, which you can download and read about here:

http://pytseries.sourceforge.net/

You should also use the dateutil package for parsing the date string, which you can find here:

http://labix.org/python-dateutil

Then you can do something like this

import datetime
import dateutil.parser
import scikits.timeseries as TS
m1 = TS.Date('M', datetime=dateutil.parser.parse('Jan-2010'))
m2 = TS.Date('M', datetime=dateutil.parser.parse('Mar-2010'))
d1 = m1.asfreq('D', relation='START') # returns a TS.Date object
d2 = m2.asfreq('D', relation='END')

firstDay = d1.datetime
lastDay = d2.datetime

This solution is dependent out outside modules, but they're very powerful and well written.

Greg