diff --git a/pandas/_libs/tslibs/period.pyi b/pandas/_libs/tslibs/period.pyi index 690518a9fa88f..8826757e31c32 100644 --- a/pandas/_libs/tslibs/period.pyi +++ b/pandas/_libs/tslibs/period.pyi @@ -3,6 +3,7 @@ from typing import Literal import numpy as np +from pandas._libs.tslibs.dtypes import PeriodDtypeBase from pandas._libs.tslibs.nattype import NaTType from pandas._libs.tslibs.offsets import BaseOffset from pandas._libs.tslibs.timestamps import Timestamp @@ -67,6 +68,7 @@ class PeriodMixin: class Period(PeriodMixin): ordinal: int # int64_t freq: BaseOffset + _dtype: PeriodDtypeBase # error: "__new__" must return a class instance (got "Union[Period, NaTType]") def __new__( # type: ignore[misc] diff --git a/pandas/_libs/tslibs/period.pyx b/pandas/_libs/tslibs/period.pyx index 6ec99fc47dba9..0f2a5fe89d7bb 100644 --- a/pandas/_libs/tslibs/period.pyx +++ b/pandas/_libs/tslibs/period.pyx @@ -1711,7 +1711,7 @@ cdef class _Period(PeriodMixin): def __richcmp__(self, other, op): if is_period_object(other): - if other.freq != self.freq: + if other._dtype != self._dtype: if op == Py_EQ: return False elif op == Py_NE: @@ -1781,7 +1781,7 @@ cdef class _Period(PeriodMixin): elif other is NaT: return NaT elif util.is_integer_object(other): - ordinal = self.ordinal + other * self.freq.n + ordinal = self.ordinal + other * self._dtype._n return Period(ordinal=ordinal, freq=self.freq) elif is_period_object(other): @@ -1866,7 +1866,7 @@ cdef class _Period(PeriodMixin): # self.n can't be negative or 0 end = how == "E" if end: - ordinal = self.ordinal + self.freq.n - 1 + ordinal = self.ordinal + self._dtype._n - 1 else: ordinal = self.ordinal ordinal = period_asfreq(ordinal, base1, base2, end) @@ -2630,7 +2630,7 @@ class Period(_Period): elif is_period_object(value): other = value - if freq is None or freq._period_dtype_code == other.freq._period_dtype_code: + if freq is None or freq._period_dtype_code == other._dtype._dtype_code: ordinal = other.ordinal freq = other.freq else: diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index b01b8e91a2cc7..d428f003f6a68 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -1309,7 +1309,7 @@ def __add__(self, other): if not isinstance(self.dtype, PeriodDtype): raise integer_op_not_supported(self) obj = cast("PeriodArray", self) - result = obj._addsub_int_array_or_scalar(other * obj.freq.n, operator.add) + result = obj._addsub_int_array_or_scalar(other * obj.dtype._n, operator.add) # array-like others elif is_timedelta64_dtype(other_dtype): @@ -1327,7 +1327,7 @@ def __add__(self, other): if not isinstance(self.dtype, PeriodDtype): raise integer_op_not_supported(self) obj = cast("PeriodArray", self) - result = obj._addsub_int_array_or_scalar(other * obj.freq.n, operator.add) + result = obj._addsub_int_array_or_scalar(other * obj.dtype._n, operator.add) else: # Includes Categorical, other ExtensionArrays # For PeriodDtype, if self is a TimedeltaArray and other is a @@ -1367,7 +1367,7 @@ def __sub__(self, other): if not isinstance(self.dtype, PeriodDtype): raise integer_op_not_supported(self) obj = cast("PeriodArray", self) - result = obj._addsub_int_array_or_scalar(other * obj.freq.n, operator.sub) + result = obj._addsub_int_array_or_scalar(other * obj.dtype._n, operator.sub) elif isinstance(other, Period): result = self._sub_periodlike(other) @@ -1391,7 +1391,7 @@ def __sub__(self, other): if not isinstance(self.dtype, PeriodDtype): raise integer_op_not_supported(self) obj = cast("PeriodArray", self) - result = obj._addsub_int_array_or_scalar(other * obj.freq.n, operator.sub) + result = obj._addsub_int_array_or_scalar(other * obj.dtype._n, operator.sub) else: # Includes ExtensionArrays, float_dtype return NotImplemented diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index 03d7efcbb1685..c455f91656909 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -104,7 +104,7 @@ def _field_accessor(name: str, docstring: str | None = None): def f(self): - base = self.freq._period_dtype_code + base = self.dtype._dtype_code result = get_period_field_arr(name, self.asi8, base) return result @@ -543,7 +543,7 @@ def to_timestamp(self, freq=None, how: str = "start") -> DatetimeArray: diffs = libalgos.unique_deltas(self.asi8) if len(diffs) == 1: diff = diffs[0] - if diff == self.freq.n: + if diff == self.dtype._n: dta._freq = self.freq elif diff == 1: dta._freq = self.freq.base @@ -614,7 +614,7 @@ def asfreq(self, freq=None, how: str = "E") -> Self: # self.freq.n can't be negative or 0 end = how == "E" if end: - ordinal = asi8 + self.freq.n - 1 + ordinal = asi8 + self.dtype._n - 1 else: ordinal = asi8 diff --git a/pandas/core/indexes/period.py b/pandas/core/indexes/period.py index 2a087612245be..f693f9557ecdc 100644 --- a/pandas/core/indexes/period.py +++ b/pandas/core/indexes/period.py @@ -435,18 +435,7 @@ def get_loc(self, key): raise KeyError(orig_key) from err def _disallow_mismatched_indexing(self, key: Period) -> None: - sfreq = self.freq - kfreq = key.freq - if not ( - sfreq.n == kfreq.n - # error: "BaseOffset" has no attribute "_period_dtype_code" - and sfreq._period_dtype_code # type: ignore[attr-defined] - # error: "BaseOffset" has no attribute "_period_dtype_code" - == kfreq._period_dtype_code # type: ignore[attr-defined] - ): - # GH#42247 For the subset of DateOffsets that can be Period freqs, - # checking these two attributes is sufficient to check equality, - # and much more performant than `self.freq == key.freq` + if key._dtype != self.dtype: raise KeyError(key) def _cast_partial_indexing_scalar(self, label: datetime) -> Period: