diff --git a/pandas/tests/tseries/holiday/test_holiday.py b/pandas/tests/tseries/holiday/test_holiday.py index b2eefd04ef93b..08f4a1250392e 100644 --- a/pandas/tests/tseries/holiday/test_holiday.py +++ b/pandas/tests/tseries/holiday/test_holiday.py @@ -271,6 +271,25 @@ def test_both_offset_observance_raises(): ) +def test_list_of_list_of_offsets_raises(): + # see gh-29049 + # Test that the offsets of offsets are forbidden + holiday1 = Holiday( + "Holiday1", + month=USThanksgivingDay.month, + day=USThanksgivingDay.day, + offset=[USThanksgivingDay.offset, DateOffset(1)], + ) + msg = "Only BaseOffsets and flat lists of them are supported for offset." + with pytest.raises(ValueError, match=msg): + Holiday( + "Holiday2", + month=holiday1.month, + day=holiday1.day, + offset=[holiday1.offset, DateOffset(3)], + ) + + def test_half_open_interval_with_observance(): # Prompted by GH 49075 # Check for holidays that have a half-open date interval where diff --git a/pandas/tseries/holiday.py b/pandas/tseries/holiday.py index cc9e2e3be8c38..8e51183138b5c 100644 --- a/pandas/tseries/holiday.py +++ b/pandas/tseries/holiday.py @@ -4,6 +4,7 @@ datetime, timedelta, ) +from typing import Callable import warnings from dateutil.relativedelta import ( @@ -17,6 +18,7 @@ ) import numpy as np +from pandas._libs.tslibs.offsets import BaseOffset from pandas.errors import PerformanceWarning from pandas import ( @@ -159,24 +161,34 @@ def __init__( year=None, month=None, day=None, - offset=None, - observance=None, + offset: BaseOffset | list[BaseOffset] | None = None, + observance: Callable | None = None, start_date=None, end_date=None, - days_of_week=None, + days_of_week: tuple | None = None, ) -> None: """ Parameters ---------- name : str Name of the holiday , defaults to class name - offset : array of pandas.tseries.offsets or - class from pandas.tseries.offsets - computes offset from date - observance: function - computes when holiday is given a pandas Timestamp - days_of_week: - provide a tuple of days e.g (0,1,2,3,) for Monday Through Thursday + year : int, default None + Year of the holiday + month : int, default None + Month of the holiday + day : int, default None + Day of the holiday + offset : list of pandas.tseries.offsets or + class from pandas.tseries.offsets, default None + Computes offset from date + observance : function, default None + Computes when holiday is given a pandas Timestamp + start_date : datetime-like, default None + First date the holiday is observed + end_date : datetime-like, default None + Last date the holiday is observed + days_of_week : tuple of int or dateutil.relativedelta weekday strs, default None + Provide a tuple of days e.g (0,1,2,3,) for Monday Through Thursday Monday=0,..,Sunday=6 Examples @@ -216,8 +228,19 @@ class from pandas.tseries.offsets >>> July3rd Holiday: July 3rd (month=7, day=3, ) """ - if offset is not None and observance is not None: - raise NotImplementedError("Cannot use both offset and observance.") + if offset is not None: + if observance is not None: + raise NotImplementedError("Cannot use both offset and observance.") + if not ( + isinstance(offset, BaseOffset) + or ( + isinstance(offset, list) + and all(isinstance(off, BaseOffset) for off in offset) + ) + ): + raise ValueError( + "Only BaseOffsets and flat lists of them are supported for offset." + ) self.name = name self.year = year