diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index fb07e3fe7547e..fa5ef0e7adf59 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -598,7 +598,7 @@ cdef class BaseOffset: def _get_offset_day(self, other: datetime) -> int: # subclass must implement `_day_opt`; calling from the base class - # will raise NotImplementedError. + # will implicitly assume day_opt = "business_end", see get_day_of_month. cdef: npy_datetimestruct dts pydate_to_dtstruct(other, &dts) @@ -3651,7 +3651,6 @@ def shift_months(const int64_t[:] dtindex, int months, object day_opt=None): out[i] = dtstruct_to_dt64(&dts) elif day_opt in ["start", "end", "business_start", "business_end"]: _shift_months(dtindex, out, count, months, day_opt) - else: raise ValueError("day must be None, 'start', 'end', " "'business_start', or 'business_end'") @@ -3841,7 +3840,7 @@ def shift_month(stamp: datetime, months: int, day_opt: object=None) -> datetime: return stamp.replace(year=year, month=month, day=day) -cdef inline int get_day_of_month(npy_datetimestruct* dts, day_opt) nogil except? -1: +cdef inline int get_day_of_month(npy_datetimestruct* dts, str day_opt) nogil: """ Find the day in `other`'s month that satisfies a DateOffset's is_on_offset policy, as described by the `day_opt` argument. @@ -3867,27 +3866,23 @@ cdef inline int get_day_of_month(npy_datetimestruct* dts, day_opt) nogil except? >>> get_day_of_month(other, 'end') 30 + Notes + ----- + Caller is responsible for ensuring one of the four accepted day_opt values + is passed. """ - cdef: - int days_in_month if day_opt == "start": return 1 elif day_opt == "end": - days_in_month = get_days_in_month(dts.year, dts.month) - return days_in_month + return get_days_in_month(dts.year, dts.month) elif day_opt == "business_start": # first business day of month return get_firstbday(dts.year, dts.month) - elif day_opt == "business_end": + else: + # i.e. day_opt == "business_end": # last business day of month return get_lastbday(dts.year, dts.month) - elif day_opt is not None: - raise ValueError(day_opt) - elif day_opt is None: - # Note: unlike `shift_month`, get_day_of_month does not - # allow day_opt = None - raise NotImplementedError cpdef int roll_convention(int other, int n, int compare) nogil: @@ -3941,6 +3936,10 @@ def roll_qtrday(other: datetime, n: int, month: int, cdef: int months_since npy_datetimestruct dts + + if day_opt not in ["start", "end", "business_start", "business_end"]: + raise ValueError(day_opt) + pydate_to_dtstruct(other, &dts) if modby == 12: diff --git a/pandas/tests/tseries/offsets/test_offsets.py b/pandas/tests/tseries/offsets/test_offsets.py index 784c04f225630..cffaa7b43d0cf 100644 --- a/pandas/tests/tseries/offsets/test_offsets.py +++ b/pandas/tests/tseries/offsets/test_offsets.py @@ -4310,12 +4310,6 @@ def test_all_offset_classes(self, tup): # --------------------------------------------------------------------- -def test_get_offset_day_error(): - # subclass of _BaseOffset must override _day_opt attribute, or we should - # get a NotImplementedError - - with pytest.raises(NotImplementedError): - DateOffset()._get_offset_day(datetime.now()) def test_valid_default_arguments(offset_types):