I'm attempting to compute the 5th working day into the future, based on a slightly customized calendar. However, the values for Labor Day (a US holiday in Sept) are computing incorrectly, while the values for the months of January and July (the other months potentially impacted by a holiday) are computed correctly.

I have already tried using dts + 5 * custom_bday - but that simply moves the problem from Sept values being incorrect, to having January and July values off by a day.

import pandas as pd
from datetime import datetime

start = datetime(2019,1,1)
end = datetime(2024,8,1)
dtrange = pd.date_range(start, end, freq='MS')
dts = pd.Series(dtrange)

hols = holidays.UnitedStates(state='MD', years=[2019,2020,2021,2022,2023,2024])
notholidays = ['American Indian Heritage Day', 'Columbus Day', 'Veterans Day']
realhols = [i for i in sorted(hols.items()) if i[1] not in notholidays]
realholsdt = [i[0] for i in realhols]

custom_bday = pd.offsets.CustomBusinessDay(holidays=realholsdt)

dts + 4 * custom_bday

The expected results are a Series of the 5th workdays of each month, excluding weekends and holidays. However all Labor Day holidays (in Sept) are off, for example

Seeing: 8 2019-09-06

Expecting: 8 2019-09-09

1 Answers

0
Chris On

You can get the first business day of the month through CustomBusinessMonthBegin, while accounting for a CustomUSHolidayCalendar. In your custom calendar, make sure you correctly offset LaborDay (if that's what you want), and add/remove any holidays. Use it within your date_range by adding it to the freq argument.

import pandas as pd
from datetime import datetime, timedelta

from pandas.tseries.offsets import CustomBusinessMonthBegin, BDay
from pandas.tseries.holiday import (
    AbstractHolidayCalendar, DateOffset,
    Holiday, MO, nearest_workday,
    USLaborDay, USMemorialDay, USColumbusDay, USThanksgivingDay,
    USPresidentsDay, EasterMonday, USMartinLutherKingJr
)

class CustomUSHolidayCalendar(AbstractHolidayCalendar):
    rules = [
        Holiday('New Years Day', month=1, day=1, observance=nearest_workday),
        USMartinLutherKingJr,
        USPresidentsDay,
        USMemorialDay,
        Holiday('July 4th', month=7, day=4, observance=nearest_workday),

        # offset labor day
        Holiday('Labor Day', month=9, day=5, offset=DateOffset(weekday=MO(1))),

        # Holiday('Veterans Day', month=11, day=11, observance=nearest_workday),
        USThanksgivingDay,
        Holiday('Christmas', month=12, day=25, observance=nearest_workday),
        Holiday('Columbus Day', month=10, day=1, offset = pd.DateOffset(weekday=MO(2))),
    ]

start_date = datetime(2019, 1, 1)
end_date = datetime(2024, 8, 1)

usholidays = CustomUSHolidayCalendar()
# US Labor day as 2019-09-09

print(usholidays.holidays(start_date, datetime(2019, 12, 1)))

custom_bday_us = CustomBusinessMonthBegin(calendar=CustomUSHolidayCalendar())
bday_over_df = pd.date_range(start=start_date,
                             end=end_date,
                             freq=custom_bday_us)

Finally, offset the first business day by 4 business days.

print(bday_over_df + 4*BDay())

DatetimeIndex(['2019-01-08', '2019-02-07', '2019-03-07', '2019-04-05',
               '2019-05-07', '2019-06-07', '2019-07-05', '2019-08-07',
               '2019-09-06', '2019-10-07', '2019-11-07', '2019-12-06',
               '2020-01-08', '2020-02-07', '2020-03-06', '2020-04-07',
               '2020-05-07', '2020-06-05', '2020-07-07', '2020-08-07',
               '2020-09-07', '2020-10-07', '2020-11-06', '2020-12-07',
               '2021-01-08', '2021-02-05', '2021-03-05', '2021-04-07',
               '2021-05-07', '2021-06-07', '2021-07-07', '2021-08-06',
               '2021-09-07', '2021-10-07', '2021-11-05', '2021-12-07',
               '2022-01-07', '2022-02-07', '2022-03-07', '2022-04-07',
               '2022-05-06', '2022-06-07', '2022-07-07', '2022-08-05',
               '2022-09-07', '2022-10-07', '2022-11-07', '2022-12-07',
               '2023-01-09', '2023-02-07', '2023-03-07', '2023-04-07',
               '2023-05-05', '2023-06-07', '2023-07-07', '2023-08-07',
               '2023-09-07', '2023-10-06', '2023-11-07', '2023-12-07',
               '2024-01-08', '2024-02-07', '2024-03-07', '2024-04-05',
               '2024-05-07', '2024-06-07', '2024-07-05', '2024-08-07'],
              dtype='datetime64[ns]', freq=None)