diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index fc5bd626a712f..e872d1683ae34 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -988,13 +988,17 @@ def delta_to_tick(delta: timedelta) -> Tick: # -------------------------------------------------------------------- -class BusinessMixin(SingleConstructorOffset): +cdef class BusinessMixin(SingleConstructorOffset): """ Mixin to business types to provide related functions. """ + + cdef readonly: + timedelta _offset + def __init__(self, n=1, normalize=False, offset=timedelta(0)): BaseOffset.__init__(self, n, normalize) - object.__setattr__(self, "_offset", offset) + self._offset = offset @property def offset(self): @@ -1014,6 +1018,11 @@ class BusinessMixin(SingleConstructorOffset): out += ": " + ", ".join(attrs) return out + cpdef __setstate__(self, state): + # We need to use a cdef/cpdef method to set the readonly _offset attribute + BaseOffset.__setstate__(self, state) + self._offset = state["_offset"] + class BusinessHourMixin(BusinessMixin): _adjust_dst = False @@ -1067,6 +1076,9 @@ class BusinessHourMixin(BusinessMixin): object.__setattr__(self, "start", start) object.__setattr__(self, "end", end) + def __reduce__(self): + return type(self), (self.n, self.normalize, self.start, self.end, self.offset) + def _repr_attrs(self) -> str: out = super()._repr_attrs() hours = ",".join( @@ -1299,7 +1311,7 @@ cdef class QuarterOffset(SingleConstructorOffset): return type(dtindex)._simple_new(shifted, dtype=dtindex.dtype) -cdef class MonthOffset(BaseOffset): +cdef class MonthOffset(SingleConstructorOffset): def is_on_offset(self, dt) -> bool: if self.normalize and not is_normalized(dt): return False @@ -1316,13 +1328,6 @@ cdef class MonthOffset(BaseOffset): shifted = shift_months(dtindex.asi8, self.n, self._day_opt) return type(dtindex)._simple_new(shifted, dtype=dtindex.dtype) - @classmethod - def _from_name(cls, suffix=None): - # default _from_name calls cls with no args - if suffix: - raise ValueError(f"Bad freq suffix {suffix}") - return cls() - # ---------------------------------------------------------------------- # RelativeDelta Arithmetic diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index ae745181692b8..1163a43f7da98 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -790,7 +790,7 @@ def __init__( # Month-Based Offset Classes -class MonthEnd(SingleConstructorMixin, liboffsets.MonthOffset): +class MonthEnd(liboffsets.MonthOffset): """ DateOffset of one month end. """ @@ -799,7 +799,7 @@ class MonthEnd(SingleConstructorMixin, liboffsets.MonthOffset): _day_opt = "end" -class MonthBegin(SingleConstructorMixin, liboffsets.MonthOffset): +class MonthBegin(liboffsets.MonthOffset): """ DateOffset of one month at beginning. """ @@ -808,7 +808,7 @@ class MonthBegin(SingleConstructorMixin, liboffsets.MonthOffset): _day_opt = "start" -class BusinessMonthEnd(SingleConstructorMixin, liboffsets.MonthOffset): +class BusinessMonthEnd(liboffsets.MonthOffset): """ DateOffset increments between business EOM dates. """ @@ -817,7 +817,7 @@ class BusinessMonthEnd(SingleConstructorMixin, liboffsets.MonthOffset): _day_opt = "business_end" -class BusinessMonthBegin(SingleConstructorMixin, liboffsets.MonthOffset): +class BusinessMonthBegin(liboffsets.MonthOffset): """ DateOffset of one business month at beginning. """ @@ -827,9 +827,7 @@ class BusinessMonthBegin(SingleConstructorMixin, liboffsets.MonthOffset): @doc(bound="bound") -class _CustomBusinessMonth( - CustomMixin, BusinessMixin, SingleConstructorMixin, liboffsets.MonthOffset -): +class _CustomBusinessMonth(CustomMixin, BusinessMixin, liboffsets.MonthOffset): """ DateOffset subclass representing custom business month(s). @@ -871,6 +869,13 @@ def __init__( BusinessMixin.__init__(self, n, normalize, offset) CustomMixin.__init__(self, weekmask, holidays, calendar) + def __reduce__(self): + # None for self.calendar bc np.busdaycalendar doesnt pickle nicely + return ( + type(self), + (self.n, self.normalize, self.weekmask, self.holidays, None, self.offset), + ) + @cache_readonly def cbday_roll(self): """