18

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!

tkyass
  • 2,968
  • 8
  • 38
  • 57
  • here's [the code for the last weekday of the next month](http://stackoverflow.com/a/29783682/4279). If you want to take into account holidays; you need a database for a given country e.g., [`holidays`](https://pypi.python.org/pypi/holidays) (I don't know how complete or up-to-date it is). – jfs May 26 '16 at 13:15
  • related: [Business days in Python](http://stackoverflow.com/q/2224742/4279) – jfs May 26 '16 at 13:17

9 Answers9

34

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)
Elke
  • 491
  • 4
  • 9
  • How would you go about with a custom calendar? Like European business days in germany? – WJA Jan 23 '19 at 16:01
  • 2
    You can define your own business month with the pandas offset function `CustomBusinessMonthEnd`. You can find documentation here: http://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.tseries.offsets.CustomBusinessMonthEnd.html#pandas.tseries.offsets.CustomBusinessMonthEnd – Elke Jan 28 '19 at 01:38
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])
VBobCat
  • 2,527
  • 4
  • 29
  • 56
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')
nyan314sn
  • 1,766
  • 3
  • 17
  • 29
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.

d84_n1nj4
  • 1,712
  • 6
  • 23
  • 40
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.

Tanu
  • 1,503
  • 12
  • 21
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)
zephyr707
  • 173
  • 1
  • 8
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
nhboe
  • 11
  • 1
0

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')
gregV
  • 987
  • 9
  • 28
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
UCCH
  • 1
  • 1