Skip to content
Merged
13 changes: 8 additions & 5 deletions pandas-stubs/_libs/tslibs/period.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ from pandas import (
from typing_extensions import TypeAlias

from pandas._libs.tslibs import NaTType
from pandas._libs.tslibs.offsets import BaseOffset
from pandas._libs.tslibs.offsets import (
BaseOffset,
)
from pandas._libs.tslibs.timestamps import Timestamp
from pandas._typing import (
PeriodFrequency,
ShapeT,
np_1darray,
np_ndarray,
Expand Down Expand Up @@ -62,7 +65,7 @@ class Period(PeriodMixin):
value: (
Period | str | datetime.datetime | datetime.date | Timestamp | None
) = ...,
freq: str | BaseOffset | None = ...,
freq: PeriodFrequency | None = None,
ordinal: int | None = ...,
year: int | None = ...,
month: int | None = ...,
Expand Down Expand Up @@ -226,12 +229,12 @@ class Period(PeriodMixin):
def day_of_year(self) -> int: ...
@property
def day_of_week(self) -> int: ...
def asfreq(self, freq: str | BaseOffset, how: _PeriodFreqHow = "end") -> Period: ...
def asfreq(self, freq: PeriodFrequency, how: _PeriodFreqHow = "end") -> Period: ...
@classmethod
def now(cls, freq: str | BaseOffset = ...) -> Period: ...
def now(cls, freq: PeriodFrequency | None = None) -> Period: ...
def strftime(self, fmt: str) -> str: ...
def to_timestamp(
self,
freq: str | BaseOffset | None = ...,
freq: PeriodFrequency | None = None,
how: _PeriodToTimestampHow = "S",
) -> Timestamp: ...
8 changes: 4 additions & 4 deletions pandas-stubs/_libs/tslibs/timedeltas.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ from typing_extensions import (
)

from pandas._libs.tslibs import (
BaseOffset,
NaTType,
)
from pandas._libs.tslibs.period import Period
from pandas._libs.tslibs.timestamps import Timestamp
from pandas._typing import (
Frequency,
ShapeT,
TimeUnit,
np_1darray,
Expand Down Expand Up @@ -130,9 +130,9 @@ class Timedelta(timedelta):
@property
def asm8(self) -> np.timedelta64: ...
# TODO: round/floor/ceil could return NaT?
def round(self, freq: str | BaseOffset) -> Self: ...
def floor(self, freq: str | BaseOffset) -> Self: ...
def ceil(self, freq: str | BaseOffset) -> Self: ...
def round(self, freq: Frequency) -> Self: ...
def floor(self, freq: Frequency) -> Self: ...
def ceil(self, freq: Frequency) -> Self: ...
Comment on lines -135 to +137
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docs say str but offset objects are also accepted, so I think it's ok to keep the current annotation

In [11]: pd.Timedelta(minutes=37).round(pd.tseries.offsets.Hour())
Out[11]: Timedelta('0 days 01:00:00')

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth a question in the pandas repo so we can make sure we are not allowing a behavior that is not fully supported. Otherwise the rest looks good, @Dr-Irv do you see anything else prior to merging?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@loicdiridollou I'm going to trust your review process rather than having me look at it!

@property
def resolution_string(self) -> str: ...
# Override due to more types supported than dt.timedelta
Expand Down
4 changes: 2 additions & 2 deletions pandas-stubs/_libs/tslibs/timestamps.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ from typing_extensions import (
)

from pandas._libs.tslibs import (
BaseOffset,
Period,
Tick,
Timedelta,
)
from pandas._typing import (
PeriodFrequency,
ShapeT,
TimestampNonexistent,
TimeUnit,
Expand Down Expand Up @@ -282,7 +282,7 @@ class Timestamp(datetime, SupportsIndex):
def is_year_end(self) -> bool: ...
def to_pydatetime(self, warn: bool = ...) -> datetime: ...
def to_datetime64(self) -> np.datetime64: ...
def to_period(self, freq: BaseOffset | str | None = ...) -> Period: ...
def to_period(self, freq: PeriodFrequency | None = ...) -> Period: ...
def to_julian_date(self) -> np.float64: ...
@property
def asm8(self) -> np.datetime64: ...
Expand Down
27 changes: 27 additions & 0 deletions pandas-stubs/_typing.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,19 @@ from pandas.core.dtypes.dtypes import (
)

from pandas.io.formats.format import EngFormatter
from pandas.tseries.offsets import (
Day,
Hour,
Micro,
Milli,
Minute,
MonthEnd,
Nano,
QuarterEnd,
Second,
Week,
YearEnd,
)

P = ParamSpec("P")

Expand Down Expand Up @@ -162,6 +175,20 @@ Suffixes: TypeAlias = tuple[str | None, str | None] | list[str | None]
Ordered: TypeAlias = bool | None
JSONSerializable: TypeAlias = PythonScalar | list | dict
Frequency: TypeAlias = str | BaseOffset
PeriodFrequency: TypeAlias = (
str
| Day
| Hour
| Minute
| Second
| Milli
| Micro
| Nano
| YearEnd
| QuarterEnd
| MonthEnd
| Week
)
Axes: TypeAlias = ListLike

RandomState: TypeAlias = (
Expand Down
5 changes: 4 additions & 1 deletion pandas-stubs/core/arrays/period.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ from pandas.core.arrays.datetimelike import (

from pandas._libs.tslibs import Timestamp
from pandas._libs.tslibs.period import Period
from pandas._typing import PeriodFrequency

class PeriodArray(DatetimeLikeArrayMixin, DatelikeOps):
__array_priority__: int = ...
Expand Down Expand Up @@ -37,6 +38,8 @@ class PeriodArray(DatetimeLikeArrayMixin, DatelikeOps):
def start_time(self) -> Timestamp: ...
@property
def end_time(self) -> Timestamp: ...
def to_timestamp(self, freq: str | None = ..., how: str = ...) -> Timestamp: ...
def to_timestamp(
self, freq: PeriodFrequency | None = None, how: str = ...
) -> Timestamp: ...
def asfreq(self, freq: str | None = ..., how: str = "E") -> Period: ...
def astype(self, dtype, copy: bool = True): ...
13 changes: 7 additions & 6 deletions pandas-stubs/core/frame.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ from pandas._libs.lib import _NoDefaultDoNotUse
from pandas._libs.missing import NAType
from pandas._libs.tslibs import BaseOffset
from pandas._libs.tslibs.nattype import NaTType
from pandas._libs.tslibs.offsets import DateOffset
from pandas._typing import (
S2,
AggFuncTypeBase,
Expand All @@ -106,6 +105,7 @@ from pandas._typing import (
FillnaOptions,
FloatFormatType,
FormattersType,
Frequency,
GroupByObjectNonScalar,
HashableT,
HashableT1,
Expand Down Expand Up @@ -135,6 +135,7 @@ from pandas._typing import (
NDFrameT,
NsmallestNlargestKeep,
ParquetEngine,
PeriodFrequency,
QuantileInterpolation,
RandomState,
ReadBuffer,
Expand Down Expand Up @@ -1683,14 +1684,14 @@ class DataFrame(NDFrame, OpsMixin, _GetItemHack):
) -> Self: ...
def to_timestamp(
self,
freq=...,
freq: PeriodFrequency | None = None,
Comment on lines 1682 to +1685
Copy link
Member Author

@MarcoGorelli MarcoGorelli Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i've always found bizarre that to_timestamp only accepts period frequencies

In [4]: pd.Period('2020').to_timestamp('MS')
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[4], line 1
----> 1 pd.Period('2020').to_timestamp('MS')

File ~/scratch/.39venv/lib/python3.9/site-packages/pandas/_libs/tslibs/period.pyx:1868, in pandas._libs.tslibs.period._Period.to_timestamp()

AttributeError: 'pandas._libs.tslibs.offsets.MonthBegin' object has no attribute '_period_dtype_code'

(and, on newer versions of pandas):

In [2]: pd.Period('2020').to_timestamp('MS')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
File pandas/_libs/tslibs/offsets.pyx:5363, in pandas._libs.tslibs.offsets.to_offset()

File pandas/_libs/tslibs/offsets.pyx:5246, in pandas._libs.tslibs.offsets._validate_to_offset_alias()

ValueError: for Period, please use 'M' instead of 'MS'

The above exception was the direct cause of the following exception:

ValueError                                Traceback (most recent call last)
Cell In[2], line 1
----> 1 pd.Period('2020').to_timestamp('MS')

File pandas/_libs/tslibs/period.pyx:2041, in pandas._libs.tslibs.period._Period.to_timestamp()

File pandas/_libs/tslibs/period.pyx:1771, in pandas._libs.tslibs.period._Period._maybe_convert_freq()

File pandas/_libs/tslibs/offsets.pyx:5402, in pandas._libs.tslibs.offsets.to_offset()

ValueError: Invalid frequency: MS, failed to parse with error message: ValueError("for Period, please use 'M' instead of 'MS'")

but whatever, if that's how it is then it should at least be typed to respect that 🤷

how: ToTimestampHow = ...,
axis: Axis = 0,
copy: _bool = True,
) -> Self: ...
def to_period(
self,
freq: _str | None = None,
freq: PeriodFrequency | None = None,
axis: Axis = 0,
copy: _bool = True,
) -> Self: ...
Expand Down Expand Up @@ -2224,7 +2225,7 @@ class DataFrame(NDFrame, OpsMixin, _GetItemHack):
self,
periods: int = 1,
fill_method: None = None,
freq: DateOffset | dt.timedelta | _str | None = ...,
freq: Frequency | dt.timedelta | None = ...,
Comment on lines -2225 to +2226
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the typing here was too tight to begin with

In [6]: s = pd.Series([1,2,3], index=pd.date_range('2020', freq='MS', periods=3))

In [7]: s.pct_change(freq='MS')
Out[7]:
2020-01-01    NaN
2020-02-01    1.0
2020-03-01    0.5
Freq: MS, dtype: float64

fill_value: Scalar | NAType | None = ...,
) -> Self: ...
def pop(self, item: _str) -> Series: ...
Expand Down Expand Up @@ -2341,7 +2342,7 @@ class DataFrame(NDFrame, OpsMixin, _GetItemHack):
@overload
def rolling(
self,
window: int | str | dt.timedelta | BaseOffset | BaseIndexer,
window: int | Frequency | dt.timedelta | BaseIndexer,
min_periods: int | None = ...,
center: _bool = ...,
on: Hashable | None = ...,
Expand All @@ -2355,7 +2356,7 @@ class DataFrame(NDFrame, OpsMixin, _GetItemHack):
@overload
def rolling(
self,
window: int | str | dt.timedelta | BaseOffset | BaseIndexer,
window: int | Frequency | dt.timedelta | BaseIndexer,
min_periods: int | None = ...,
center: _bool = ...,
on: Hashable | None = ...,
Expand Down
2 changes: 1 addition & 1 deletion pandas-stubs/core/groupby/groupby.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ class GroupBy(BaseGroupBy[NDFrameT]):
periods: int = ...,
fill_method: Literal["bfill", "ffill"] | None | _NoDefaultDoNotUse = ...,
limit: int | None | _NoDefaultDoNotUse = ...,
freq=...,
freq: Frequency | None = None,
axis: Axis | _NoDefaultDoNotUse = ...,
) -> NDFrameT: ...
@final
Expand Down
15 changes: 8 additions & 7 deletions pandas-stubs/core/indexes/accessors.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ from typing_extensions import Never

from pandas._libs.interval import Interval
from pandas._libs.tslibs import BaseOffset
from pandas._libs.tslibs.offsets import DateOffset
from pandas._libs.tslibs.period import Period
from pandas._libs.tslibs.timedeltas import Timedelta
from pandas._libs.tslibs.timestamps import Timestamp
from pandas._typing import (
Frequency,
PeriodFrequency,
TimeAmbiguous,
TimeNonexistent,
TimestampConvention,
Expand Down Expand Up @@ -178,7 +179,7 @@ _DTTimestampTimedeltaReturnType = TypeVar(
class _DatetimeRoundingMethods(Generic[_DTTimestampTimedeltaReturnType]):
def round(
self,
freq: str | BaseOffset | None,
freq: Frequency | None,
ambiguous: Literal["raise", "infer", "NaT"] | bool | np_ndarray_bool = ...,
nonexistent: (
Literal["shift_forward", "shift_backward", "NaT", "raise"]
Expand All @@ -188,7 +189,7 @@ class _DatetimeRoundingMethods(Generic[_DTTimestampTimedeltaReturnType]):
) -> _DTTimestampTimedeltaReturnType: ...
def floor(
self,
freq: str | BaseOffset | None,
freq: Frequency | None,
ambiguous: Literal["raise", "infer", "NaT"] | bool | np_ndarray_bool = ...,
nonexistent: (
Literal["shift_forward", "shift_backward", "NaT", "raise"]
Expand All @@ -198,7 +199,7 @@ class _DatetimeRoundingMethods(Generic[_DTTimestampTimedeltaReturnType]):
) -> _DTTimestampTimedeltaReturnType: ...
def ceil(
self,
freq: str | BaseOffset | None,
freq: Frequency | None,
ambiguous: Literal["raise", "infer", "NaT"] | bool | np_ndarray_bool = ...,
nonexistent: (
Literal["shift_forward", "shift_backward", "NaT", "raise"]
Expand All @@ -225,7 +226,7 @@ class _DatetimeLikeNoTZMethods(
],
):
def to_period(
self, freq: str | BaseOffset | None = ...
self, freq: PeriodFrequency | None = None
) -> _DTToPeriodReturnType: ...
def tz_localize(
self,
Expand Down Expand Up @@ -357,12 +358,12 @@ class _PeriodProperties(
def strftime(self, date_format: str) -> _PeriodStrReturnTypes: ...
def to_timestamp(
self,
freq: str | DateOffset | None = ...,
freq: PeriodFrequency | None = None,
how: TimestampConvention = ...,
) -> _PeriodDTAReturnTypes: ...
def asfreq(
self,
freq: str | DateOffset | None = ...,
freq: PeriodFrequency | None = None,
how: Literal["E", "END", "FINISH", "S", "START", "BEGIN"] = ...,
) -> _PeriodPAReturnTypes: ...

Expand Down
14 changes: 6 additions & 8 deletions pandas-stubs/core/indexes/datetimes.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import numpy as np
from pandas import (
DataFrame,
Index,
Timedelta,
TimedeltaIndex,
Timestamp,
)
Expand All @@ -25,7 +24,6 @@ from pandas.core.indexes.datetimelike import DatetimeTimedeltaMixin
from pandas.core.series import Series
from typing_extensions import Self

from pandas._libs.tslibs.offsets import DateOffset
from pandas._typing import (
AxesData,
DateAndDatetimeLike,
Expand Down Expand Up @@ -99,14 +97,14 @@ class DatetimeIndex(
@property
def dtype(self) -> np.dtype | DatetimeTZDtype: ...
def shift(
self, periods: int = 1, freq: DateOffset | Timedelta | str | None = None
self, periods: int = 1, freq: Frequency | timedelta | None = None
) -> Self: ...

@overload
def date_range(
start: str | DateAndDatetimeLike,
end: str | DateAndDatetimeLike,
freq: str | timedelta | Timedelta | BaseOffset | None = None,
freq: Frequency | timedelta | None = None,
tz: TimeZones = None,
normalize: bool = False,
name: Hashable | None = None,
Expand All @@ -129,7 +127,7 @@ def date_range(
start: str | DateAndDatetimeLike,
*,
periods: int,
freq: str | timedelta | Timedelta | BaseOffset | None = None,
freq: Frequency | timedelta | None = None,
tz: TimeZones = None,
normalize: bool = False,
name: Hashable | None = None,
Expand All @@ -141,7 +139,7 @@ def date_range(
*,
end: str | DateAndDatetimeLike,
periods: int,
freq: str | timedelta | Timedelta | BaseOffset | None = None,
freq: Frequency | timedelta | None = None,
tz: TimeZones = None,
normalize: bool = False,
name: Hashable | None = None,
Expand All @@ -153,7 +151,7 @@ def bdate_range(
start: str | DateAndDatetimeLike | None = ...,
end: str | DateAndDatetimeLike | None = ...,
periods: int | None = ...,
freq: str | timedelta | Timedelta | BaseOffset = ...,
freq: Frequency | timedelta = ...,
tz: TimeZones = ...,
normalize: bool = ...,
name: Hashable | None = ...,
Expand All @@ -167,7 +165,7 @@ def bdate_range(
end: str | DateAndDatetimeLike | None = ...,
periods: int | None = ...,
*,
freq: str | timedelta | Timedelta | BaseOffset,
freq: Frequency | timedelta,
tz: TimeZones = ...,
normalize: bool = ...,
name: Hashable | None = ...,
Expand Down
Loading