Skip to content

BUG: Raise error if not busdaycalendar #61250

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions doc/source/user_guide/timeseries.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1065,9 +1065,9 @@ Holiday calendars can be used to provide the list of holidays. See the

.. ipython:: python

from pandas.tseries.holiday import USFederalHolidayCalendar
calendar = np.busdaycalendar(holidays=['2014-01-01', '2014-01-20'])

bday_us = pd.offsets.CustomBusinessDay(calendar=USFederalHolidayCalendar())
bday_us = pd.offsets.CustomBusinessDay(calendar=calendar)

# Friday before MLK Day
dt = datetime.datetime(2014, 1, 17)
Expand All @@ -1080,7 +1080,8 @@ in the usual way.

.. ipython:: python

bmth_us = pd.offsets.CustomBusinessMonthBegin(calendar=USFederalHolidayCalendar())
bdd = np.busdaycalendar(holidays=['2011-07-01', '2011-07-04', '2011-07-17'])
bmth_us = pd.offsets.CustomBusinessMonthBegin(calendar=bdd)

# Skip new years
dt = datetime.datetime(2013, 12, 17)
Expand Down Expand Up @@ -1210,9 +1211,9 @@ as ``BusinessHour`` except that it skips specified custom holidays.

.. ipython:: python

from pandas.tseries.holiday import USFederalHolidayCalendar
calendar = np.busdaycalendar(holidays=['2014-01-01', '2014-01-20'])

bhour_us = pd.offsets.CustomBusinessHour(calendar=USFederalHolidayCalendar())
bhour_us = pd.offsets.CustomBusinessHour(calendar=calendar)
# Friday before MLK Day
dt = datetime.datetime(2014, 1, 17, 15)

Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ Other Removals
- Disallow indexing an :class:`Index` with a boolean indexer of length zero, it now raises ``ValueError`` (:issue:`55820`)
- Disallow non-standard (``np.ndarray``, :class:`Index`, :class:`ExtensionArray`, or :class:`Series`) to :func:`isin`, :func:`unique`, :func:`factorize` (:issue:`52986`)
- Disallow passing a pandas type to :meth:`Index.view` (:issue:`55709`)
- Disallow passing objects other than ``np.busdaycalendar`` to ``calendar`` parameter in :class:`CustomBusinessDay` (:issue:`60647`)
- Disallow units other than "s", "ms", "us", "ns" for datetime64 and timedelta64 dtypes in :func:`array` (:issue:`53817`)
- Removed "freq" keyword from :class:`PeriodArray` constructor, use "dtype" instead (:issue:`52462`)
- Removed 'fastpath' keyword in :class:`Categorical` constructor (:issue:`20110`)
Expand Down
23 changes: 19 additions & 4 deletions pandas/_libs/tslibs/offsets.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -4666,6 +4666,11 @@ cdef class CustomBusinessDay(BusinessDay):
offset=timedelta(0),
):
BusinessDay.__init__(self, n, normalize, offset)
if calendar is not None and not isinstance(calendar, np.busdaycalendar):
raise TypeError(
f"Only np.busdaycalendar is supported for calendar, "
f"got {type(calendar).__name__} instead"
)
self._init_custom(weekmask, holidays, calendar)

cpdef __setstate__(self, state):
Expand Down Expand Up @@ -4822,6 +4827,11 @@ cdef class CustomBusinessHour(BusinessHour):
offset=timedelta(0),
):
BusinessHour.__init__(self, n, normalize, start=start, end=end, offset=offset)
if calendar is not None and not isinstance(calendar, np.busdaycalendar):
raise TypeError(
f"Only np.busdaycalendar is supported for calendar, "
f"got {type(calendar).__name__} instead"
)
self._init_custom(weekmask, holidays, calendar)


Expand All @@ -4840,6 +4850,11 @@ cdef class _CustomBusinessMonth(BusinessMixin):
offset=timedelta(0),
):
BusinessMixin.__init__(self, n, normalize, offset)
if calendar is not None and not isinstance(calendar, np.busdaycalendar):
raise TypeError(
f"Only np.busdaycalendar is supported for calendar, "
f"got {type(calendar).__name__} instead"
)
self._init_custom(weekmask, holidays, calendar)

@cache_readonly
Expand Down Expand Up @@ -5108,8 +5123,8 @@ def _warn_about_deprecated_aliases(name: str, is_period: bool) -> str:
warnings.warn(
f"\'{name}\' is deprecated and will be removed "
f"in a future version, please use "
f"\'{c_PERIOD_AND_OFFSET_DEPR_FREQSTR.get(name)}\'"
f" instead.",
f"\'{c_PERIOD_AND_OFFSET_DEPR_FREQSTR.get(name)}\' "
f"instead.",
FutureWarning,
stacklevel=find_stack_level(),
)
Expand All @@ -5122,8 +5137,8 @@ def _warn_about_deprecated_aliases(name: str, is_period: bool) -> str:
warnings.warn(
f"\'{name}\' is deprecated and will be removed "
f"in a future version, please use "
f"\'{_name}\'"
f" instead.",
f"\'{_name}\' "
f"instead.",
FutureWarning,
stacklevel=find_stack_level(),
)
Expand Down
7 changes: 2 additions & 5 deletions pandas/tests/tseries/holiday/test_calendar.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from datetime import datetime

import numpy as np
import pytest

from pandas import (
Expand All @@ -14,7 +15,6 @@
Holiday,
Timestamp,
USFederalHolidayCalendar,
USLaborDay,
USThanksgivingDay,
get_calendar,
)
Expand Down Expand Up @@ -97,10 +97,7 @@ def test_calendar_2031():
# Labor Day 2031 is on September 1. Saturday before is August 30.
# Next working day after August 30 ought to be Tuesday, September 2.

class testCalendar(AbstractHolidayCalendar):
rules = [USLaborDay]

cal = testCalendar()
cal = np.busdaycalendar(holidays=["2031-09-01"])
workDay = offsets.CustomBusinessDay(calendar=cal)
Sat_before_Labor_Day_2031 = to_datetime("2031-08-30")
next_working_day = Sat_before_Labor_Day_2031 + 0 * workDay
Expand Down
16 changes: 12 additions & 4 deletions pandas/tests/tseries/offsets/test_custom_business_day.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@
import numpy as np
import pytest

from pandas._libs.tslibs.offsets import CDay
from pandas._libs.tslibs.offsets import (
BDay,
CDay,
CustomBusinessDay,
)

from pandas import (
_testing as tm,
read_pickle,
)
from pandas.tests.tseries.offsets.common import assert_offset_equal

from pandas.tseries.holiday import USFederalHolidayCalendar


@pytest.fixture
def offset():
Expand Down Expand Up @@ -78,7 +80,7 @@ def test_weekmask_and_holidays(self):

@pytest.mark.filterwarnings("ignore:Non:pandas.errors.PerformanceWarning")
def test_calendar(self):
calendar = USFederalHolidayCalendar()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So was this just getting ignored before? If not and the behavior was working as expected, we should continue supporting these arguments

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my understanding, rhshadrach thinks we should only support np.busdaycalender for the argument calendar. He said in the issue, "I think we should raise when a calendar that is not an np.busdaycalender is passed instead of silently ignoring it.".

Therefore, I have edited the constructor to raise a TypeError if anything other than None or a np.busdaycalender is passed. Happy to change if needed!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I agree in principle, but with the modified docs and tests in this PR we have some testing with AbstractHolidayCalendar and it's subclasses, so this PR would introduce breaking behavior.

Therefore, I would suggest first investigating if those docs/tests are producing correct behavior. If so, I would suggest calling the original issue a bug that needs fixing, but it would still be good to add validation to allow calendar: None | np.businesscalendar | AbstractHolidayCalendar

calendar = np.busdaycalendar(holidays=["2014-01-01", "2014-01-20"])
dt = datetime(2014, 1, 17)
assert_offset_equal(CDay(calendar=calendar), dt, datetime(2014, 1, 21))

Expand All @@ -97,3 +99,9 @@ def test_pickle_compat_0_14_1(self, datapath):
cday0_14_1 = read_pickle(pth)
cday = CDay(holidays=hdays)
assert cday == cday0_14_1

def test_type_error_calendar(self):
bd = BDay(1)
msg = "Only np.busdaycalendar is supported for calendar"
with pytest.raises(TypeError, match=msg):
CustomBusinessDay(calendar=bd)
5 changes: 2 additions & 3 deletions pandas/tests/tseries/offsets/test_custom_business_hour.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@

from pandas.tests.tseries.offsets.common import assert_offset_equal

from pandas.tseries.holiday import USFederalHolidayCalendar

holidays = ["2014-06-27", datetime(2014, 6, 30), np.datetime64("2014-07-02")]


Expand Down Expand Up @@ -299,7 +297,8 @@ def test_apply_nanoseconds(self, nano_case):

def test_us_federal_holiday_with_datetime(self):
# GH 16867
bhour_us = CustomBusinessHour(calendar=USFederalHolidayCalendar())
calendar = np.busdaycalendar(holidays=["2014-01-01", "2014-01-20"])
bhour_us = CustomBusinessHour(calendar=calendar)
t0 = datetime(2014, 1, 17, 15)
result = t0 + bhour_us * 8
expected = Timestamp("2014-01-21 15:00:00")
Expand Down
Loading