Business days in Python

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP


Business days in Python



I need to subtract business days from the current date.



I currently have some code which needs always to be running on the most recent business day. So that may be today if we're Monday thru Friday, but if it's Saturday or Sunday then I need to set it back to the Friday before the weekend. I currently have some pretty clunky code to do this:


lastBusDay = datetime.datetime.today()
if datetime.date.weekday(lastBusDay) == 5: #if it's Saturday
lastBusDay = lastBusDay - datetime.timedelta(days = 1) #then make it Friday
elif datetime.date.weekday(lastBusDay) == 6: #if it's Sunday
lastBusDay = lastBusDay - datetime.timedelta(days = 2); #then make it Friday



Is there a better way?



Can I tell timedelta to work in weekdays rather than calendar days for example?





What about holidays?
– SLaks
Feb 8 '10 at 20:49





Here's a snippet from dzzone that might help you out: snippets.dzone.com/posts/show/9173
– David Underhill
Feb 8 '10 at 20:50





yeah I'm already taking care of those: my database always backfills holidays as long as they fall on a weekday. But I agree, holidays in general are also an issue. I mean I could start getting fancy and use the sckits.timeseries but really I want something simpler.
– Thomas Browne
Feb 8 '10 at 20:51





hello, i'm late to the party, sorry. one simpler thing OP could have done is checking whether datetime.date.weekday(lastBusDay) >=5 instead of checking Saturday and Sunday separatedly. but yeah .. there are other better answer below, anyway.
– tagoma
Mar 19 '16 at 8:55






9 Answers
9



Use pandas!


import pandas as pd
# BDay is business day, not birthday...
from pandas.tseries.offsets import BDay

# pd.datetime is an alias for datetime.datetime
today = pd.datetime.today()
print today - BDay(4)



Since today is Thursday, Sept 26, that will give you an output of:


datetime.datetime(2013, 9, 20, 14, 8, 4, 89761)





Nice. that's the right answer, today. When I asked the Q, pandas was still a little incomplete.
– Thomas Browne
Oct 6 '13 at 11:15





The most recent pandas release (0.14.0) supports holiday calendars too
– fantabolous
Jun 3 '14 at 7:01



There seem to be several options if you're open to installing extra libraries.



This post describes a way of defining workdays with dateutil.



http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-09/3758.html



BusinessHours lets you custom-define your list of holidays, etc., to define when your working hours (and by extension working days) are.



http://pypi.python.org/pypi/BusinessHours/





nice one Alison. Still not very simple though unfortunately. I'm going to go your route though. Thanks for the help.
– Thomas Browne
Feb 8 '10 at 21:02





Defining business days across all cultures is unlikely to be simple enough a problem to get included in the standard library.
– Alison R.
Feb 8 '10 at 21:06





A valid point, as indeed my application is for financial markets, and Egypt and Israel are open on Sunday.
– Thomas Browne
Feb 9 '10 at 20:20







Does anyone even use BuinessHours? Within 1 minute I discovered that line 87 should read self.worktiming[1] (missing self) and line 51 should read extradays (missing s). The source code itself looks pretty poor with semicolons scattered throughout.
– Pakman
Jul 19 '14 at 21:00




self.worktiming[1]


extradays



DISCLAMER: I'm the author...



I wrote a package that does exactly this, business dates calculations. You can use custom week specification and holidays.



I had this exact problem while working with financial data and didn't find any of the available solutions particularly easy, so I wrote one.



Hope this is useful for other people.



https://pypi.python.org/pypi/business_calendar/





thanks a lot, your library worked perfectly for me. Maybe you should add on your documentation that your library support negative days if you want to subtract days, and that it is up on pip
– guinunez
Oct 16 '14 at 12:18





Hey, I know this might not be the best way to reach you but I just wanted to report an issue I was having with your business_calendar module. I set up a calendar with US federal holidays: ['2015-01-01', '2015-01-19', '2015-02-16', '2015-05-25', '2015-07-03', '2015-09-07', '2015-10-12', '2015-11-11', '2015-11-26', '2015-12-25'] then tried to calculate the difference between datetime(2015, 1, 16, 15, 28, 40) and datetime(2015, 1, 23, 11, 58, 0) but it consistently returns -1. Stripping the h/m/s from the datetimes results (correctly) in 4.
– Darren Ringer
Feb 27 '15 at 15:26







Actually upon further testing certain date comparisons just block, with no apparent reason, never seeming to return a result. In my above example attempting to compare datetime(2015, 1, 16) with datetime(2015, 1, 25) leads to such a block, with or without explicitly setting any holidays. Upon investigation this occurs when the date2 parameter happens to be a date that is not a work day.
– Darren Ringer
Mar 2 '15 at 19:52





busdaycount also seems to block. Rather disappointing
– Andre
Oct 1 '15 at 16:40



Maybe this code could help:


lastBusDay = datetime.datetime.today()
shift = datetime.timedelta(max(1,(lastBusDay.weekday() + 6) % 7 - 3))
lastBusDay = lastBusDay - shift



The idea is that on Mondays yo have to go back 3 days, on Sundays 2, and 1 in any other day.



The statement (lastBusDay.weekday() + 6) % 7 just re-bases the Monday from 0 to 6.


(lastBusDay.weekday() + 6) % 7



Really don't know if this will be better in terms of performance.



timeboard package does this.


timeboard



Suppose your date is 04 Sep 2017. In spite of being a Monday, it was a holiday in the US (the Labor Day). So, the most recent business day was Friday, Sep 1.


>>> import timeboard.calendars.US as US
>>> clnd = US.Weekly8x5()
>>> clnd('04 Sep 2017').rollback().to_timestamp().date()
datetime.date(2017, 9, 1)



In UK, 04 Sep 2017 was the regular business day, so the most recent business day was itself.


>>> import timeboard.calendars.UK as UK
>>> clnd = UK.Weekly8x5()
>>> clnd('04 Sep 2017').rollback().to_timestamp().date()
datetime.date(2017, 9, 4)



DISCLAIMER: I am the author of timeboard.



This will give a generator of working days, of course without holidays, stop is datetime.datetime object. If you need holidays just make additional argument with list of holidays and check with 'IFology' ;-)


def workingdays(stop, start=datetime.date.today()):
while start != stop:
if start.weekday() < 5:
yield start
start += datetime.timedelta(1)



Later on you can count them like


workdays = workingdays(datetime.datetime(2015, 8, 8))
len(list(workdays))



Why don't you try something like:


lastBusDay = datetime.datetime.today()
if datetime.date.weekday(lastBusDay) not in range(0,5):
lastBusDay = 5


def getNthBusinessDay(startDate, businessDaysInBetween):
currentDate = startDate
daysToAdd = businessDaysInBetween
while daysToAdd > 0:
currentDate += relativedelta(days=1)
day = currentDate.weekday()
if day < 5:
daysToAdd -= 1

return currentDate



If you want to skip US holidays as well as weekends, this worked for me (using pandas 0.23.3):


import pandas as pd
from pandas.tseries.holiday import USFederalHolidayCalendar
from pandas.tseries.offsets import CustomBusinessDay
US_BUSINESS_DAY = CustomBusinessDay(calendar=USFederalHolidayCalendar())
july_5 = pd.datetime(2018, 7, 5)
result = july_5 - 2 * US_BUSINESS_DAY # 2018-7-2



To convert to a python date object I did this:


result.to_pydatetime().date()






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Keycloak server returning user_not_found error when user is already imported with LDAP

PHP parse/syntax errors; and how to solve them?

How to scale/resize CVPixelBufferRef in objective C, iOS