I have run into a problem with some code I have been writing. I take in four inputs ( day, month and year ) as a date, and times for how many times they want to repeat the task ( eg. every Monday for 3 weeks ). The code is great however if the weeks differ between months I get this error:
File "C:\Users\dansi\AppData\Local\Programs\Python\Python36-32\gui test 3.py", line 72, in addtimeslot
fulldateadd = datetime.date(year, month, day)
ValueError: day is out of range for month
Part of code that is relevant:
for i in range(0 , times):
fulldateadd = datetime.date(year, month, day)
cursor.execute( '''INSERT INTO dates (Date, Name, Start, End) VALUES( ?,?,?,? );''', (fulldateadd , name1, starttimehour, endtimehour))
day = day + 7
if day > 31:
month = month + 1
I've tried to increment the month when the number of days are more than 31 however it doesn't seem to work.
There are several reasons why incrementing the components of a datetime and then creating a new one is not a good idea. Primarily because dealing with the Gregorian calendar yourself isn't that enjoyable IMHO, and datetime objects can do it for you.
On that note, a much more straightforward approach would be to add a timedelta to your datetime within the loop. For instance,
>>> from datetime import timedelta
>>> times = 4
>>> cur_date = datetime.date(2017, 2, 24)
>>> for _ in range(times):
print('today is {0}, do something'.format(cur_date))
cur_date += timedelta(days=7)
today is 2017-02-24, do something
today is 2017-03-03, do something
today is 2017-03-10, do something
today is 2017-03-17, do something
This could be placed in a generator as well, depending on your use case.
>>> for dt in (cur_date + timedelta(days=x*7) for x in range(times)):
print('today is {0}, do something'.format(dt))
today is 2017-02-24, do something
today is 2017-03-03, do something
today is 2017-03-10, do something
today is 2017-03-17, do something
or with Pandas pd.date_range
>>> import pandas as pd
>>> list(pd.date_range(start='2017-02-24', periods=4, freq='7D'))
[Timestamp('2017-02-24 00:00:00', freq='7D'),
Timestamp('2017-03-03 00:00:00', freq='7D'),
Timestamp('2017-03-10 00:00:00', freq='7D'),
Timestamp('2017-03-17 00:00:00', freq='7D')]
Now what would happen if you attempted this example with your approach?
>>> year, month, day = 2017, 2, 24
>>> for i in range(0 , times):
day = day
fulldateadd = datetime.date(year, month, day)
print('today is {0}, do something'.format(fulldateadd))
day = day + 7
if day > 31:
day = day - 31
month = month + 1
today is 2017-02-24, do something
ValueErrorTraceback (most recent call last)
<ipython-input-255-7df608ebbf8e> in <module>()
1 for i in range(0 , times):
2 day = day
----> 3 fulldateadd = datetime.date(year, month, day)
4 print('today is {0}, do something'.format(fulldateadd))
5 day = day + 7
ValueError: day is out of range for month
February doesn't have 31 days... so you would have to include a check with a mapping to the number of days in each month. Including logic for leap years.
Related
I am trying to get the weeks of the current month. I can get the number of the week in that month(current_w) but I will need to get a list in the correct order. For example, for July, it will be w = [27, 28, 29, 30] because current week is in the third order of the 4 weeks of this month. Can somebody help me on that?
import datetime
kw = datetime.datetime.today().isocalendar()[1]
weeks = [7,14,21,28]
for idx, i in enumerate(weeks):
delta = datetime.timedelta(days=i)
if (a - delta).month != a.month:
current_w = idx
You could use datetime to get both the first and the last week and then just everything in between.
import datetime
import calendar
year = datetime.datetime.now().year
month = datetime.datetime.now().month
first_week = datetime.datetime(year, month, calendar.monthrange(year, month)[0]).isocalendar()[1]
last_week = datetime.datetime(year, month, calendar.monthrange(year, month)[1]).isocalendar()[1]
print(list(range(first_week, last_week + 1)))
from datetime import datetime would save you all the datetime.datetime usage btw.
If you only want weeks that are fully included in a month, the first week will be the one that contains the 7th of that month. And the last week will one before the one that contains the 1st of next month.
That is enough to give the following function:
def weeks_in_month(month, year=None):
"""Returns the (ISO) weeks fully contained in a month.
month is an integer between 1 and 12. Year is the current year
if not given"""
if year is None:
year = datetime.date.today().year
first = datetime.date(year, month, 7).isocalendar()[1]
nextd = datetime.date(year + 1, 1, 1) if month == 12 \
else datetime.date(year, month + 1, 1)
nextw = nextd.isocalendar()[1]
return list(range(first, nextw))
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?
Use pandas!
import datetime
# BDay is business day, not birthday...
from pandas.tseries.offsets import BDay
today = datetime.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)
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()
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.
Really don't know if this will be better in terms of performance.
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/
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/
If somebody is looking for solution respecting holidays (without any huge library like pandas), try this function:
import holidays
import datetime
def previous_working_day(check_day_, holidays=holidays.US()):
offset = max(1, (check_day_.weekday() + 6) % 7 - 3)
most_recent = check_day_ - datetime.timedelta(offset)
if most_recent not in holidays:
return most_recent
else:
return previous_working_day(most_recent, holidays)
check_day = datetime.date(2020, 12, 28)
previous_working_day(check_day)
which produces:
datetime.date(2020, 12, 24)
timeboard package does this.
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.
For the pandas usecase, I found the following to be quite useful and compact, although not completely readable:
Get most recent previous business day:
In [2]: datetime.datetime(2019, 11, 30) + BDay(1) - BDay(1) # Saturday
Out[2]: Timestamp('2019-11-29 00:00:00')
In [3]: datetime.datetime(2019, 11, 29) + BDay(1) - BDay(1) # Friday
Out[3]: Timestamp('2019-11-29 00:00:00')
In the other direction, simply use:
In [4]: datetime.datetime(2019, 11, 30) + BDay(0) # Saturday
Out[4]: Timestamp('2019-12-02 00:00:00')
In [5]: datetime.datetime(2019, 11, 29) + BDay(0) # Friday
Out[5]: Timestamp('2019-11-29 00:00:00')
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))
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
When I am writing this answer, today is Friday in USA so next business day shall be Monday, in the meantime yesterday is thanksgiving holiday so previous business day should be Wednesday
So today date of Friday, November 24, 2022, is a perfect time to get the previous, current and next business days.
By having trial and error, I could only find the correct output by combining the method as below:
from datetime import datetime, timedelta
from pandas.tseries.offsets import BDay
from pandas.tseries.offsets import CustomBusinessDay
from pandas.tseries.holiday import USFederalHolidayCalendar
US_BUSINESS_DAY = CustomBusinessDay(calendar=USFederalHolidayCalendar())
TODAY = datetime.today() - 1 * US_BUSINESS_DAY
YESTERDAY = (datetime.today() - timedelta(max(1,(TODAY.weekday() + 6) % 7 - 3))) - 1 * US_BUSINESS_DAY
TOMORROW = TODAY + BDay(1)
DAY_NAME = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday','Sunday']
BUSINESS_DATE = "[Previous (" + DAY_NAME[YESTERDAY.weekday()] + "):'" + YESTERDAY.strftime('%y%m%d')
BUSINESS_DATE += "', Current (" + DAY_NAME[TODAY.weekday()] + "):'" + TODAY.strftime('%y%m%d')
BUSINESS_DATE += "', Next (" + DAY_NAME[TOMORROW.weekday()] + "):'" + TOMORROW.strftime('%y%m%d') + "']"
print_("Business Date USA = ", BUSINESS_DATE)
Output:
Business Date USA = [Previous (Wednesday):'221123', Current (Friday):'221125', Next (Monday):'221128']
Getting the most recent business day:
pd.bdate_range(end=(pd.to_datetime('today').date()), periods=1)[0])
OR in case you want it as a 'datetime.date' type:
(pd.bdate_range(end=(pd.to_datetime('today').date()), periods=1)[0]).date()
The accepted answer actually gives an incorrect result because today - BDay(0) rounds forward to Monday during the weekend instead of back to Friday like the question states. What you'd want is BusinessDay().rollback() which rolls back to the prior business day (the accepted answer matches BusinessDay().rollforward() logic).
import pandas as pd
import datetime
today = datetime.datetime.today()
prior_bday = pd.tseries.offsets.BusinessDay().rollback(today)
Why don't you try something like:
lastBusDay = datetime.datetime.today()
if datetime.date.weekday(lastBusDay) not in range(0,5):
lastBusDay = 5
another simplify version
lastBusDay = datetime.datetime.today()
wk_day = datetime.date.weekday(lastBusDay)
if wk_day > 4: #if it's Saturday or Sunday
lastBusDay = lastBusDay - datetime.timedelta(days = wk_day-4) #then make it Friday
Solution irrespective of different jurisdictions having different holidays:
If you need to find the right id within a table, you can use this snippet. The Table model is a sqlalchemy model and the dates to search from are in the field day.
def last_relevant_date(db: Session, given_date: date) -> int:
available_days = (db.query(Table.id, Table.day)
.order_by(desc(Table.day))
.limit(100).all())
close_dates = pd.DataFrame(available_days)
close_dates['delta'] = close_dates['day'] - given_date
past_dates = (close_dates
.loc[close_dates['delta'] < pd.Timedelta(0, unit='d')])
table_id = int(past_dates.loc[past_dates['delta'].idxmax()]['id'])
return table_id
This is not a solution that I would recommend when you have to convert in bulk. It is rather generic and expensive as you are not using joins. Moreover, it assumes that you have a relevant day that is one of the 100 most recent days in the model Table. So it tackles data input that may have different dates.
Get first day of month, last day of month and last business day of previous month if last day falls on weekend Saturday/Sunday
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
day = datetime(2023, 1, 10)
#last day of (n) previous month (n=months)
#n = 0 -- for current month
n=1
lastDayMonth = ((day - relativedelta(months=n) + relativedelta(day=31)).date());
#First day of previous month (n=months=1)
firstDayMonth = ((day - relativedelta(months=n) + relativedelta(day=1)).date());
print("Last Day of Month - "+ str(lastDayMonth))
print("First Day of Month - "+ str(firstDayMonth))
#Last business day (Friday) of prev (n) month (n=months=1)
lastBusDay = (lastDayMonth - timedelta(max(1,(lastDayMonth.weekday() + 6) % 7 - 3))) if lastDayMonth.weekday() in (5,6) else lastDayMonth
print("Last Business Day of Month - " + str(lastBusDay))
print()
--- Output
Last Day of Month - 2022-12-31
First Day of Month - 2022-12-01
Last Business Day of Month - 2022-12-30
How can I print the date of the last Tuesday of each month for next year using Python.
For example the first line outputted would be: 30/Jan/2018
I do not want to have the full name of the month only the first 3 characters!
Currently I have figured out how to get the next year:
import datetime
now = datetime.datetime.now()
next_year = now.year + 1
Can anyone please help?
The calendar module is perfect for this:
You can use calendar.month_abbr which is an array of
abbreviated months just like you want.
week is an array representing the days of the week starting at Monday so Tuesday would be week[1].
import calendar
import datetime
now = datetime.datetime.now()
next_year = now.year + 1
for month in range(1, 13):
last_tuesday = max(week[1] for week in calendar.monthcalendar(next_year, month))
print('{}/{}/{}'.format(last_tuesday, calendar.month_abbr[month], next_year))
Output:
30/Jan/2018
27/Feb/2018
27/Mar/2018
24/Apr/2018
29/May/2018
26/Jun/2018
31/Jul/2018
28/Aug/2018
25/Sep/2018
30/Oct/2018
27/Nov/2018
25/Dec/2018
I would also suggest the pandas DateOffset object LastWeekOfMonth.
Describes monthly dates in last week of month like "the last Tuesday
of each month"
from pandas.tseries.offsets import LastWeekOfMonth
def last_tues(year):
return (pd.date_range('1/1/' + str(year), periods=12, freq='M')
- LastWeekOfMonth(n=1, weekday=1)).strftime('%d/%b/%Y'))
last_tues(2018)
Out[31]:
array(['30/Jan/2018', '27/Feb/2018', '27/Mar/2018', '24/Apr/2018',
'29/May/2018', '26/Jun/2018', '26/Jun/2018', '28/Aug/2018',
'25/Sep/2018', '30/Oct/2018', '27/Nov/2018', '25/Dec/2018'],
dtype='<U11')
Getting error for December month.
ValueError: month must be in 1..12
def last_day_of_month(ds):
cur_ds = datetime.strptime(ds, '%Y-%m-%d')
next_month = datetime(year=cur_ds.year, month=cur_ds.month+1, day=1)
last_day_month = next_month - timedelta(days=1)
return datetime.strftime(last_day_month, '%Y-%m-%d')
print last_day_of_month('2016-12-01')
In line 3 month=cur_ds.month+1 you are giving 13th month which is not valid. If you want to calculate last day of a given month you could also use month range from calendar library.
>>import calendar
>>year, month = 2016, 12
>>calendar.monthrange(year, month)[1]
31
You can't make a datetime with a month of 13. So you have to find a way to fix it. A simple solution is to convert the incremented month to an extra year:
# Reduce 12 to 1, 0 and all other #s to 0, #
extrayear, month = divmod(cur_ds.month, 12)
# Add 1 or 0 to existing year, add one to month (which was reduced to 0-11)
next_month = datetime(year=cur_ds.year + extrayear, month=month + 1, day=1)
You're passing in 12 as current month, then adding one to get next_month, making it 13. Check for the 12 case and set month=1 instead.
this is how I did it.
from django.utils import timezone
from calendar import monthrange
from datetime import datetime
current = timezone.now()
firstdayofmonth = current.replace(day=1)
endmonth = monthrange(current.year, current.month)
lastdayofmonth = datetime(current.year, current.month, endmonth[1])
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?
Use pandas!
import datetime
# BDay is business day, not birthday...
from pandas.tseries.offsets import BDay
today = datetime.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)
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()
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.
Really don't know if this will be better in terms of performance.
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/
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/
If somebody is looking for solution respecting holidays (without any huge library like pandas), try this function:
import holidays
import datetime
def previous_working_day(check_day_, holidays=holidays.US()):
offset = max(1, (check_day_.weekday() + 6) % 7 - 3)
most_recent = check_day_ - datetime.timedelta(offset)
if most_recent not in holidays:
return most_recent
else:
return previous_working_day(most_recent, holidays)
check_day = datetime.date(2020, 12, 28)
previous_working_day(check_day)
which produces:
datetime.date(2020, 12, 24)
timeboard package does this.
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.
For the pandas usecase, I found the following to be quite useful and compact, although not completely readable:
Get most recent previous business day:
In [2]: datetime.datetime(2019, 11, 30) + BDay(1) - BDay(1) # Saturday
Out[2]: Timestamp('2019-11-29 00:00:00')
In [3]: datetime.datetime(2019, 11, 29) + BDay(1) - BDay(1) # Friday
Out[3]: Timestamp('2019-11-29 00:00:00')
In the other direction, simply use:
In [4]: datetime.datetime(2019, 11, 30) + BDay(0) # Saturday
Out[4]: Timestamp('2019-12-02 00:00:00')
In [5]: datetime.datetime(2019, 11, 29) + BDay(0) # Friday
Out[5]: Timestamp('2019-11-29 00:00:00')
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))
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
When I am writing this answer, today is Friday in USA so next business day shall be Monday, in the meantime yesterday is thanksgiving holiday so previous business day should be Wednesday
So today date of Friday, November 24, 2022, is a perfect time to get the previous, current and next business days.
By having trial and error, I could only find the correct output by combining the method as below:
from datetime import datetime, timedelta
from pandas.tseries.offsets import BDay
from pandas.tseries.offsets import CustomBusinessDay
from pandas.tseries.holiday import USFederalHolidayCalendar
US_BUSINESS_DAY = CustomBusinessDay(calendar=USFederalHolidayCalendar())
TODAY = datetime.today() - 1 * US_BUSINESS_DAY
YESTERDAY = (datetime.today() - timedelta(max(1,(TODAY.weekday() + 6) % 7 - 3))) - 1 * US_BUSINESS_DAY
TOMORROW = TODAY + BDay(1)
DAY_NAME = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday','Sunday']
BUSINESS_DATE = "[Previous (" + DAY_NAME[YESTERDAY.weekday()] + "):'" + YESTERDAY.strftime('%y%m%d')
BUSINESS_DATE += "', Current (" + DAY_NAME[TODAY.weekday()] + "):'" + TODAY.strftime('%y%m%d')
BUSINESS_DATE += "', Next (" + DAY_NAME[TOMORROW.weekday()] + "):'" + TOMORROW.strftime('%y%m%d') + "']"
print_("Business Date USA = ", BUSINESS_DATE)
Output:
Business Date USA = [Previous (Wednesday):'221123', Current (Friday):'221125', Next (Monday):'221128']
Getting the most recent business day:
pd.bdate_range(end=(pd.to_datetime('today').date()), periods=1)[0])
OR in case you want it as a 'datetime.date' type:
(pd.bdate_range(end=(pd.to_datetime('today').date()), periods=1)[0]).date()
The accepted answer actually gives an incorrect result because today - BDay(0) rounds forward to Monday during the weekend instead of back to Friday like the question states. What you'd want is BusinessDay().rollback() which rolls back to the prior business day (the accepted answer matches BusinessDay().rollforward() logic).
import pandas as pd
import datetime
today = datetime.datetime.today()
prior_bday = pd.tseries.offsets.BusinessDay().rollback(today)
Why don't you try something like:
lastBusDay = datetime.datetime.today()
if datetime.date.weekday(lastBusDay) not in range(0,5):
lastBusDay = 5
another simplify version
lastBusDay = datetime.datetime.today()
wk_day = datetime.date.weekday(lastBusDay)
if wk_day > 4: #if it's Saturday or Sunday
lastBusDay = lastBusDay - datetime.timedelta(days = wk_day-4) #then make it Friday
Solution irrespective of different jurisdictions having different holidays:
If you need to find the right id within a table, you can use this snippet. The Table model is a sqlalchemy model and the dates to search from are in the field day.
def last_relevant_date(db: Session, given_date: date) -> int:
available_days = (db.query(Table.id, Table.day)
.order_by(desc(Table.day))
.limit(100).all())
close_dates = pd.DataFrame(available_days)
close_dates['delta'] = close_dates['day'] - given_date
past_dates = (close_dates
.loc[close_dates['delta'] < pd.Timedelta(0, unit='d')])
table_id = int(past_dates.loc[past_dates['delta'].idxmax()]['id'])
return table_id
This is not a solution that I would recommend when you have to convert in bulk. It is rather generic and expensive as you are not using joins. Moreover, it assumes that you have a relevant day that is one of the 100 most recent days in the model Table. So it tackles data input that may have different dates.
Get first day of month, last day of month and last business day of previous month if last day falls on weekend Saturday/Sunday
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
day = datetime(2023, 1, 10)
#last day of (n) previous month (n=months)
#n = 0 -- for current month
n=1
lastDayMonth = ((day - relativedelta(months=n) + relativedelta(day=31)).date());
#First day of previous month (n=months=1)
firstDayMonth = ((day - relativedelta(months=n) + relativedelta(day=1)).date());
print("Last Day of Month - "+ str(lastDayMonth))
print("First Day of Month - "+ str(firstDayMonth))
#Last business day (Friday) of prev (n) month (n=months=1)
lastBusDay = (lastDayMonth - timedelta(max(1,(lastDayMonth.weekday() + 6) % 7 - 3))) if lastDayMonth.weekday() in (5,6) else lastDayMonth
print("Last Business Day of Month - " + str(lastBusDay))
print()
--- Output
Last Day of Month - 2022-12-31
First Day of Month - 2022-12-01
Last Business Day of Month - 2022-12-30