Skip to content

Commit 88348e2

Browse files
jbrockmendelyehoshuadimarsky
authored andcommitted
REF: re-share Period methods (pandas-dev#46314)
1 parent d81a2b4 commit 88348e2

File tree

7 files changed

+35
-80
lines changed

7 files changed

+35
-80
lines changed

pandas/_libs/tslibs/dtypes.pxd

+2
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,5 @@ cdef enum PeriodDtypeCode:
7474
cdef class PeriodDtypeBase:
7575
cdef readonly:
7676
PeriodDtypeCode _dtype_code
77+
78+
cpdef int _get_to_timestamp_base(self)

pandas/_libs/tslibs/dtypes.pyi

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class PeriodDtypeBase:
1616
def from_date_offset(cls, offset: BaseOffset) -> PeriodDtypeBase: ...
1717
@property
1818
def resolution(self) -> Resolution: ...
19+
def _get_to_timestamp_base(self) -> int: ...
1920

2021
class FreqGroup(Enum):
2122
FR_ANN: int

pandas/_libs/tslibs/dtypes.pyx

+19
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,25 @@ cdef class PeriodDtypeBase:
5050
code = offset._period_dtype_code
5151
return cls(code)
5252

53+
cpdef int _get_to_timestamp_base(self):
54+
"""
55+
Return frequency code group used for base of to_timestamp against
56+
frequency code.
57+
58+
Return day freq code against longer freq than day.
59+
Return second freq code against hour between second.
60+
61+
Returns
62+
-------
63+
int
64+
"""
65+
base = <c_FreqGroup>self._dtype_code
66+
if base < FR_BUS:
67+
return FR_DAY
68+
elif FR_HR <= base <= FR_SEC:
69+
return FR_SEC
70+
return base
71+
5372

5473
_period_code_map = {
5574
# Annual freqs with various fiscal year ends.

pandas/_libs/tslibs/period.pyi

+8-5
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,14 @@ def period_ordinal(
5252
def freq_to_dtype_code(freq: BaseOffset) -> int: ...
5353
def validate_end_alias(how: str) -> Literal["E", "S"]: ...
5454

55-
class Period:
55+
class PeriodMixin:
56+
@property
57+
def end_time(self) -> Timestamp: ...
58+
@property
59+
def start_time(self) -> Timestamp: ...
60+
def _require_matching_freq(self, other, base: bool = ...) -> None: ...
61+
62+
class Period(PeriodMixin):
5663
ordinal: int # int64_t
5764
freq: BaseOffset
5865

@@ -118,9 +125,5 @@ class Period:
118125
def month(self) -> int: ...
119126
@property
120127
def year(self) -> int: ...
121-
@property
122-
def end_time(self) -> Timestamp: ...
123-
@property
124-
def start_time(self) -> Timestamp: ...
125128
def __sub__(self, other) -> Period | BaseOffset: ...
126129
def __add__(self, other) -> Period: ...

pandas/_libs/tslibs/period.pyx

+1-20
Original file line numberDiff line numberDiff line change
@@ -1545,25 +1545,6 @@ class IncompatibleFrequency(ValueError):
15451545
cdef class PeriodMixin:
15461546
# Methods shared between Period and PeriodArray
15471547

1548-
cpdef int _get_to_timestamp_base(self):
1549-
"""
1550-
Return frequency code group used for base of to_timestamp against
1551-
frequency code.
1552-
1553-
Return day freq code against longer freq than day.
1554-
Return second freq code against hour between second.
1555-
1556-
Returns
1557-
-------
1558-
int
1559-
"""
1560-
base = self._dtype._dtype_code
1561-
if base < FR_BUS:
1562-
return FR_DAY
1563-
elif FR_HR <= base <= FR_SEC:
1564-
return FR_SEC
1565-
return base
1566-
15671548
@property
15681549
def start_time(self) -> Timestamp:
15691550
"""
@@ -1861,7 +1842,7 @@ cdef class _Period(PeriodMixin):
18611842
return endpoint - Timedelta(1, 'ns')
18621843

18631844
if freq is None:
1864-
freq = self._get_to_timestamp_base()
1845+
freq = self._dtype._get_to_timestamp_base()
18651846
base = freq
18661847
else:
18671848
freq = self._maybe_convert_freq(freq)

pandas/core/arrays/period.py

+3-54
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def f(self):
104104
return property(f)
105105

106106

107-
class PeriodArray(dtl.DatelikeOps):
107+
class PeriodArray(dtl.DatelikeOps, libperiod.PeriodMixin):
108108
"""
109109
Pandas ExtensionArray for storing Period data.
110110
@@ -500,7 +500,7 @@ def to_timestamp(self, freq=None, how: str = "start") -> DatetimeArray:
500500
return (self + self.freq).to_timestamp(how="start") - adjust
501501

502502
if freq is None:
503-
freq = self._get_to_timestamp_base()
503+
freq = self._dtype._get_to_timestamp_base()
504504
base = freq
505505
else:
506506
freq = Period._maybe_convert_freq(freq)
@@ -606,8 +606,7 @@ def asfreq(self, freq=None, how: str = "E") -> PeriodArray:
606606

607607
freq = Period._maybe_convert_freq(freq)
608608

609-
# error: "BaseOffset" has no attribute "_period_dtype_code"
610-
base1 = self.freq._period_dtype_code # type: ignore[attr-defined]
609+
base1 = self._dtype._dtype_code
611610
base2 = freq._period_dtype_code
612611

613612
asi8 = self.asi8
@@ -884,56 +883,6 @@ def _check_timedeltalike_freq_compat(self, other):
884883

885884
raise raise_on_incompatible(self, other)
886885

887-
# ------------------------------------------------------------------
888-
# TODO: See if we can re-share this with Period
889-
890-
def _get_to_timestamp_base(self) -> int:
891-
"""
892-
Return frequency code group used for base of to_timestamp against
893-
frequency code.
894-
895-
Return day freq code against longer freq than day.
896-
Return second freq code against hour between second.
897-
898-
Returns
899-
-------
900-
int
901-
"""
902-
base = self._dtype._dtype_code
903-
if base < FreqGroup.FR_BUS.value:
904-
return FreqGroup.FR_DAY.value
905-
elif FreqGroup.FR_HR.value <= base <= FreqGroup.FR_SEC.value:
906-
return FreqGroup.FR_SEC.value
907-
return base
908-
909-
@property
910-
def start_time(self) -> DatetimeArray:
911-
return self.to_timestamp(how="start")
912-
913-
@property
914-
def end_time(self) -> DatetimeArray:
915-
return self.to_timestamp(how="end")
916-
917-
def _require_matching_freq(self, other, base: bool = False) -> None:
918-
# See also arrays.period.raise_on_incompatible
919-
if isinstance(other, BaseOffset):
920-
other_freq = other
921-
else:
922-
other_freq = other.freq
923-
924-
if base:
925-
condition = self.freq.base != other_freq.base
926-
else:
927-
condition = self.freq != other_freq
928-
929-
if condition:
930-
msg = DIFFERENT_FREQ.format(
931-
cls=type(self).__name__,
932-
own_freq=self.freqstr,
933-
other_freq=other_freq.freqstr,
934-
)
935-
raise IncompatibleFrequency(msg)
936-
937886

938887
def raise_on_incompatible(left, right):
939888
"""

pandas/tests/tseries/frequencies/test_freq_code.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def test_get_to_timestamp_base(freqstr, exp_freqstr):
1818
per = Period._from_ordinal(1, off)
1919
exp_code = to_offset(exp_freqstr)._period_dtype_code
2020

21-
result_code = per._get_to_timestamp_base()
21+
result_code = per._dtype._get_to_timestamp_base()
2222
assert result_code == exp_code
2323

2424

0 commit comments

Comments
 (0)