Is there a way using Python's standard library to easily determine (i.e. one function call) the last day of a given month?
If the standard library doesn't support that, does the dateutil package support this?
038 Answers
calendar.monthrange provides this information:
calendar.monthrange(year, month)
Returns weekday of first day of the month and number of days in month, for the specified year and month.
>>> import calendar >>> calendar.monthrange(2002, 1) (1, 31) >>> calendar.monthrange(2008, 2) # leap years are handled correctly (4, 29) >>> calendar.monthrange(2100, 2) # years divisible by 100 but not 400 aren't leap years (0, 28) so:
calendar.monthrange(year, month)[1] seems like the simplest way to go.
3If you don't want to import the calendar module, a simple two-step function can also be:
import datetime def last_day_of_month(any_day): # this will never fail # get close to the end of the month for any day, and add 4 days 'over' next_month = any_day.replace(day=28) + datetime.timedelta(days=4) # subtract the number of remaining 'overage' days to get last day of current month, or said programattically said, the previous day of the first of next month return next_month - datetime.timedelta(days=next_month.day) Outputs:
>>> for month in range(1, 13): ... print last_day_of_month(datetime.date(2012, month, 1)) ... 2012-01-31 2012-02-29 2012-03-31 2012-04-30 2012-05-31 2012-06-30 2012-07-31 2012-08-31 2012-09-30 2012-10-31 2012-11-30 2012-12-31 4EDIT: See @Blair Conrad's answer for a cleaner solution
>>> import datetime >>> datetime.date(2000, 2, 1) - datetime.timedelta(days=1) datetime.date(2000, 1, 31) 4This is actually pretty easy with dateutil.relativedelta. day=31 will always always return the last day of the month:
import datetime from dateutil.relativedelta import relativedelta date_in_feb = datetime.datetime(2013, 2, 21) print(datetime.datetime(2013, 2, 21) + relativedelta(day=31)) # End-of-month # datetime.datetime(2013, 2, 28, 0, 0) Install dateutil with
pip install python-datetutil 9EDIT: see my other answer. It has a better implementation than this one, which I leave here just in case someone's interested in seeing how one might "roll your own" calculator.
@John Millikin gives a good answer, with the added complication of calculating the first day of the next month.
The following isn't particularly elegant, but to figure out the last day of the month that any given date lives in, you could try:
def last_day_of_month(date): if date.month == 12: return date.replace(day=31) return date.replace(month=date.month+1, day=1) - datetime.timedelta(days=1) >>> last_day_of_month(datetime.date(2002, 1, 17)) datetime.date(2002, 1, 31) >>> last_day_of_month(datetime.date(2002, 12, 9)) datetime.date(2002, 12, 31) >>> last_day_of_month(datetime.date(2008, 2, 14)) datetime.date(2008, 2, 29) 5Using dateutil.relativedelta you would get last date of month like this:
from dateutil.relativedelta import relativedelta last_date_of_month = datetime(mydate.year, mydate.month, 1) + relativedelta(months=1, days=-1) The idea is to get the first day of the month and use relativedelta to go 1 month ahead and 1 day back so you would get the last day of the month you wanted.
In Python 3.7 there is the undocumented calendar.monthlen(year, month) function:
>>> calendar.monthlen(2002, 1) 31 >>> calendar.monthlen(2008, 2) 29 >>> calendar.monthlen(2100, 2) 28 It is equivalent to the documented calendar.monthrange(year, month)[1] call.
from datetime import timedelta (any_day.replace(day=1) + timedelta(days=32)).replace(day=1) - timedelta(days=1) 3>>> import datetime >>> import calendar >>> date = datetime.datetime.now() >>> print date 2015-03-06 01:25:14.939574 >>> print date.replace(day = 1) 2015-03-01 01:25:14.939574 >>> print date.replace(day = calendar.monthrange(date.year, date.month)[1]) 2015-03-31 01:25:14.939574 1Another solution would be to do something like this:
from datetime import datetime def last_day_of_month(year, month): """ Work out the last day of the month """ last_days = [31, 30, 29, 28, 27] for i in last_days: try: end = datetime(year, month, i) except ValueError: continue else: return end.date() return None And use the function like this:
>>> >>> last_day_of_month(2008, 2) datetime.date(2008, 2, 29) >>> last_day_of_month(2009, 2) datetime.date(2009, 2, 28) >>> last_day_of_month(2008, 11) datetime.date(2008, 11, 30) >>> last_day_of_month(2008, 12) datetime.date(2008, 12, 31) 1To get the last date of the month we do something like this:
from datetime import date, timedelta import calendar last_day = date.today().replace(day=calendar.monthrange(date.today().year, date.today().month)[1]) Now to explain what we are doing here we will break it into two parts:
first is getting the number of days of the current month for which we use monthrange which Blair Conrad has already mentioned his solution:
calendar.monthrange(date.today().year, date.today().month)[1] second is getting the last date itself which we do with the help of replace e.g
>>> date.today() datetime.date(2017, 1, 3) >>> date.today().replace(day=31) datetime.date(2017, 1, 31) and when we combine them as mentioned on the top we get a dynamic solution.
2if you are willing to use an external library, check out
U can then get the last day of the month with:
import arrow arrow.utcnow().ceil('month').date() This returns a date object which you can then do your manipulation.
Use pandas!
def isMonthEnd(date): return date + pd.offsets.MonthEnd(0) == date isMonthEnd(datetime(1999, 12, 31)) True isMonthEnd(pd.Timestamp('1999-12-31')) True isMonthEnd(pd.Timestamp(1965, 1, 10)) False 3import datetime now = datetime.datetime.now() start_month = datetime.datetime(now.year, now.month, 1) date_on_next_month = start_month + datetime.timedelta(35) start_next_month = datetime.datetime(date_on_next_month.year, date_on_next_month.month, 1) last_day_month = start_next_month - datetime.timedelta(1) Here is another answer. No extra packages required.
datetime.date(year + int(month/12), month%12+1, 1)-datetime.timedelta(days=1) Get the first day of the next month and subtract a day from it.
you can use relativedelta month_end = <your datetime value within the month> + relativedelta(day=31) that will give you the last day.
This is the simplest solution for me using just the standard datetime library:
import datetime def get_month_end(dt): first_of_month = datetime.datetime(dt.year, dt.month, 1) next_month_date = first_of_month + datetime.timedelta(days=32) new_dt = datetime.datetime(next_month_date.year, next_month_date.month, 1) return new_dt - datetime.timedelta(days=1) The easiest & most reliable way I've found so Far is as:
from datetime import datetime import calendar days_in_month = calendar.monthrange(2020, 12)[1] end_dt = datetime(2020, 12, days_in_month) The simplest way is to use datetime and some date math, e.g. subtract a day from the first day of the next month:
import datetime def last_day_of_month(d: datetime.date) -> datetime.date: return ( datetime.date(d.year + d.month % 12 + 1, 1) - datetime.timedelta(days=1) ) Alternatively, you could use calendar.monthrange() to get the number of days in a month (taking leap years into account) and update the date accordingly:
import calendar, datetime def last_day_of_month(d: datetime.date) -> datetime.date: return d.replace(day=calendar.monthrange(d.year, d.month)[1]) A quick benchmark shows that the first version is noticeably faster:
In [14]: today = datetime.date.today() In [15]: %timeit last_day_of_month_dt(today) 918 ns ± 3.54 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) In [16]: %timeit last_day_of_month_calendar(today) 1.4 µs ± 17.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) 0To me the easier way is using pandas (two lines solution):
from datetime import datetime import pandas as pd firstday_month = datetime(year, month, 1) lastday_month = firstday_month + pd.offsets.MonthEnd(1) Another way to do it is: Taking the first day of the month, then adding one month and discounting one day:
from datetime import datetime import pandas as pd firstday_month = datetime(year, month, 1) lastday_month = firstday_month + pd.DateOffset(months=1) - pd.DateOffset(days=1) That's my way - a function with only two lines:
from dateutil.relativedelta import relativedelta def last_day_of_month(date): return date.replace(day=1) + relativedelta(months=1) - relativedelta(days=1) Example:
from datetime import date print(last_day_of_month(date.today())) >> 2021-09-30 For me it's the simplest way:
selected_date = date(some_year, some_month, some_day) if selected_date.month == 12: # December last_day_selected_month = date(selected_date.year, selected_date.month, 31) else: last_day_selected_month = date(selected_date.year, selected_date.month + 1, 1) - timedelta(days=1) You can calculate the end date yourself. the simple logic is to subtract a day from the start_date of next month. :)
So write a custom method,
import datetime def end_date_of_a_month(date): start_date_of_this_month = date.replace(day=1) month = start_date_of_this_month.month year = start_date_of_this_month.year if month == 12: month = 1 year += 1 else: month += 1 next_month_start_date = start_date_of_this_month.replace(month=month, year=year) this_month_end_date = next_month_start_date - datetime.timedelta(days=1) return this_month_end_date Calling,
end_date_of_a_month(datetime.datetime.now().date()) It will return the end date of this month. Pass any date to this function. returns you the end date of that month.
The easiest way (without having to import calendar), is to get the first day of the next month, and then subtract a day from it.
import datetime as dt from dateutil.relativedelta import relativedelta thisDate = dt.datetime(2017, 11, 17) last_day_of_the_month = dt.datetime(thisDate.year, (thisDate + relativedelta(months=1)).month, 1) - dt.timedelta(days=1) print last_day_of_the_month Output:
datetime.datetime(2017, 11, 30, 0, 0) PS: This code runs faster as compared to the import calendarapproach; see below:
import datetime as dt import calendar from dateutil.relativedelta import relativedelta someDates = [dt.datetime.today() - dt.timedelta(days=x) for x in range(0, 10000)] start1 = dt.datetime.now() for thisDate in someDates: lastDay = dt.datetime(thisDate.year, (thisDate + relativedelta(months=1)).month, 1) - dt.timedelta(days=1) print ('Time Spent= ', dt.datetime.now() - start1) start2 = dt.datetime.now() for thisDate in someDates: lastDay = dt.datetime(thisDate.year, thisDate.month, calendar.monthrange(thisDate.year, thisDate.month)[1]) print ('Time Spent= ', dt.datetime.now() - start2) OUTPUT:
Time Spent= 0:00:00.097814 Time Spent= 0:00:00.109791 This code assumes that you want the date of the last day of the month (i.e., not just the DD part, but the entire YYYYMMDD date)
3Here is a long (easy to understand) version but takes care of leap years.
cheers, JK
def last_day_month(year, month): leap_year_flag = 0 end_dates = { 1: 31, 2: 28, 3: 31, 4: 30, 5: 31, 6: 30, 7: 31, 8: 31, 9: 30, 10: 31, 11: 30, 12: 31 } # Checking for regular leap year if year % 4 == 0: leap_year_flag = 1 else: leap_year_flag = 0 # Checking for century leap year if year % 100 == 0: if year % 400 == 0: leap_year_flag = 1 else: leap_year_flag = 0 else: pass # return end date of the year-month if leap_year_flag == 1 and month == 2: return 29 elif leap_year_flag == 1 and month != 2: return end_dates[month] else: return end_dates[month] 1This does not address the main question, but one nice trick to get the last weekday in a month is to use calendar.monthcalendar, which returns a matrix of dates, organized with Monday as the first column through Sunday as the last.
# Some random date. some_date = datetime.date(2012, 5, 23) # Get last weekday last_weekday = np.asarray(calendar.monthcalendar(some_date.year, some_date.month))[:,0:-2].ravel().max() print last_weekday 31 The whole [0:-2] thing is to shave off the weekend columns and throw them out. Dates that fall outside of the month are indicated by 0, so the max effectively ignores them.
The use of numpy.ravel is not strictly necessary, but I hate relying on the mere convention that numpy.ndarray.max will flatten the array if not told which axis to calculate over.
How about more simply:
import datetime now = datetime.datetime.now() datetime.date(now.year, 1 if now.month==12 else now.month+1, 1) - datetime.timedelta(days=1) Using dateutil.relativedelta
dt + dateutil.relativedelta.relativedelta(months=1, day=1, days=-1) months=1 and day=1 would shift dt to the first date of next month, then days=-1 would shift the new date to previous date which is exactly the last date of current month.
import calendar from time import gmtime, strftime calendar.monthrange(int(strftime("%Y", gmtime())), int(strftime("%m", gmtime())))[1] Output:
31 This will print the last day of whatever the current month is. In this example it was 15th May, 2016. So your output may be different, however the output will be as many days that the current month is. Great if you want to check the last day of the month by running a daily cron job.
So:
import calendar from time import gmtime, strftime lastDay = calendar.monthrange(int(strftime("%Y", gmtime())), int(strftime("%m", gmtime())))[1] today = strftime("%d", gmtime()) lastDay == today Output:
False Unless it IS the last day of the month.
I prefer this way
import datetime import calendar date=datetime.datetime.now() month_end_date=datetime.datetime(date.year,date.month,1) + datetime.timedelta(days=calendar.monthrange(date.year,date.month)[1] - 1)