19

I'm trying to find last business day of of the month. I wrote the code below for that and it works fine but I was wondering if there is a cleaner way of doing it?

from  datetime import date,timedelta
import datetime
import calendar

today=datetime.date.today()

last = today.replace(day=calendar.monthrange(today.year,today.month)[1])

if last.weekday()<5:
    print last

else:
    print last-timedelta(days=1+last.weekday()-5)

Thanks in advance!

2

9 Answers 9

36

I use the following:

from pandas.tseries.offsets import BMonthEnd
from datetime import date

d=date.today()

offset = BMonthEnd()

#Last day of current month
offset.rollforward(d)

#Last day of previous month
offset.rollback(d)
Sign up to request clarification or add additional context in comments.

3 Comments

How would you go about with a custom calendar? Like European business days in germany?
You can define your own business month with the pandas offset function CustomBusinessMonthEnd. You can find documentation here: pandas.pydata.org/pandas-docs/stable/reference/api/…
sorry but your code is not right. For d = '2023-12-29' the last line returns '2023-12-29', NOT '2023-11-30'. So it does not find last day of the PREVIOUS month.
10

For one-liner fans:

import calendar

def last_business_day_in_month(year: int, month: int) -> int:
    return max(calendar.monthcalendar(year, month)[-1][:5])

5 Comments

This way does not seem to work ... AttributeError: 'function' object has no attribute 'monthcalendar'. An edit has been proposed.
@xtheking works for me with python 3.8
@xtheking: Under what conditions is [-1:][0] different than [-1]? I think it could be shorter/clearer...
Beautiful in its cleverness! But as @resnbl implies, the [-1:][0] can be replaced with [-1].
Updated according to your suggestions.
8

Let's say you want to get the last business days of the month up-to the end of the next two years, the following will work.

   import pandas as pd
   import datetime

   start = datetime.date.today()
   end = datetime.date(start.year+2, 12, 31)
   bussiness_days_rng =pd.date_range(start, end, freq='BM')

Comments

2

I use this for the first business day of the month but it can be used for last business day of the month as well:

import time
import datetime
from pandas.tseries.holiday import USFederalHolidayCalendar
from pandas.tseries.offsets import CustomBusinessDay
from dateutil.relativedelta import relativedelta

#Create dates needed to be entered as parameters
today = datetime.date.today()
first = today.replace(day=1)
#End of the Prior Month
eopm = first - datetime.timedelta(days=1)
eopm = eopm.strftime("%Y%m%d")
#Create first business day of current month date
us_bd = CustomBusinessDay(calendar=USFederalHolidayCalendar())
focm = first
nxtMo = today + relativedelta(months=+1)
fonm = nxtMo.replace(day=1)
eocm = fonm - datetime.timedelta(days=1)
first_bd = pd.DatetimeIndex(start = focm, end = eocm, freq= us_bd)
first_bd = first_bd.strftime("%Y%m%d")
#First Business Day of the Month
first_bd = first_bd[0]
#Last Business Day of the Month
lst_day = len(first_bd)-1
last_bd = first_bd[lst_day]

I left some code in there that is not needed for the last business day of the current month, but may be useful to someone.

Comments

1

slight upgrade over @Elke's answer when today is in fact a month end, to ensure it would not get rolled:

run_date = pd.Timestamp('2023-07-31')

run_date.replace(day=1) + pd.offsets.BMonthEnd()
Out[27]: Timestamp('2023-07-31 00:00:00')

run_date.replace(day=1) - pd.offsets.BMonthEnd()
Out[28]: Timestamp('2023-06-30 00:00:00')

Comments

0

You can use Pandas to get business days. Refer http://pandas.pydata.org/pandas-docs/stable/timeseries.html

Also you can refer this https://pypi.python.org/pypi/business_calendar/ for simple business days calculation.

Comments

0

with rollforward(d) you will skip to the next month if the date is past the last business day of the current month, so below might be safer for any day of the month:

from datetime import date
import pandas as pd

d = date(2011, 12, 31) # a caturday
pd.bdate_range(end=pd.offsets.MonthEnd().rollforward(d), periods=1)
pd.offsets.BMonthEnd().rollforward(d)

Comments

0

I needed something intuitively readable and opted for the following:

from datetime import datetime, timedelta
import pandas as pd

def isMonthLastBusinessDay(date):
    lastDayOfMonth = date + pd.offsets.MonthEnd(0)
    isFriday = date.weekday() == 4
    if (date.weekday() < 5 and lastDayOfMonth == date) or (isFriday and lastDayOfMonth == date+timedelta(days=1)) or (isFriday and lastDayOfMonth == date+timedelta(days=2)):
        return True
    else:
        return False

Comments

0

Here another bit generalized function

def is_last_day_in_period(date, freq='bm'):
""" Checks if particular date is last date of a specific period

e.g. 'm', 'bm', 'q', 'bq', 'a', 'ba'

Parameters
----------
date : datelike
    date to evaluate
freq : str
    valid pandas frequency string

Returns
-------
check : bool
    if last day in period
"""

date = pd.Timestamp(date)
int_dates = pd.date_range(start=date, end=date + Day(1000), freq=freq)
check = int_dates[int_dates >= date][0] == date

return check

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.