Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pandas-stubs/_libs/tslibs/timedeltas.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class Timedelta(timedelta):
def __new__(
cls,
value: str | float | Timedelta | Tick | timedelta | np.timedelta64 = ...,
unit: TimeDeltaUnitChoices = ...,
unit: TimeDeltaUnitChoices = "ns",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In Timedelta.__new__, the unit parameter actually defaults to None at runtime, although the docstring mentions "ns". Using None allows the constructor to handle input resolution correctly.

Ground Truth: timedeltas.pyx#L2147

Gemini 3 created, I verified

*,
days: float | np.integer | np.floating = ...,
seconds: float | np.integer | np.floating = ...,
Expand Down Expand Up @@ -340,7 +340,7 @@ class Timedelta(timedelta):
def to_numpy(self) -> np.timedelta64: ...
@property
def components(self) -> Components: ...
def view(self, dtype: npt.DTypeLike = ...) -> object: ...
def view(self, dtype: npt.DTypeLike) -> object: ...
@property
def unit(self) -> TimeUnit: ...
def as_unit(self, unit: TimeUnit, round_ok: bool = True) -> Self: ...
54 changes: 27 additions & 27 deletions pandas-stubs/_libs/tslibs/timestamps.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,19 @@ class Timestamp(datetime, SupportsIndex):
def __new__(
cls,
ts_input: np.integer | float | str | _date | datetime | np.datetime64 = ...,
year: int | None = ...,
month: int | None = ...,
day: int | None = ...,
hour: int | None = ...,
minute: int | None = ...,
second: int | None = ...,
microsecond: int | None = ...,
tzinfo: _tzinfo | None = ...,
year: int | None = None,
month: int | None = None,
day: int | None = None,
hour: int | None = None,
minute: int | None = None,
second: int | None = None,
microsecond: int | None = None,
tzinfo: _tzinfo | None = None,
*,
nanosecond: int | None = ...,
tz: TimeZones = ...,
unit: str | int | None = ...,
fold: Literal[0, 1] | None = ...,
nanosecond: int | None = None,
tz: TimeZones = None,
unit: str | int | None = None,
fold: Literal[0, 1] | None = None,
) -> Self: ...
# GH 46171
# While Timestamp can return pd.NaT, having the constructor return
Expand Down Expand Up @@ -102,19 +102,19 @@ class Timestamp(datetime, SupportsIndex):
if sys.version_info >= (3, 12):
@classmethod
def fromtimestamp( # pyright: ignore[reportIncompatibleMethodOverride]
cls, t: float, tz: _tzinfo | str | None = ...
cls, t: float, tz: _tzinfo | str | None = None
) -> Self: ...
else:
@classmethod
def fromtimestamp(cls, t: float, tz: _tzinfo | str | None = ...) -> Self: ...
def fromtimestamp(cls, t: float, tz: _tzinfo | str | None = None) -> Self: ...

@classmethod
def today(cls, tz: _tzinfo | str | None = None) -> Self: ...
@classmethod
def fromordinal(
cls,
ordinal: int,
tz: _tzinfo | str | None = ...,
tz: _tzinfo | str | None = None,
) -> Self: ...
@classmethod
def now(cls, tz: _tzinfo | str | None = None) -> Self: ...
Expand All @@ -137,17 +137,17 @@ class Timestamp(datetime, SupportsIndex):
# Violation of Liskov substitution principle
def replace( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] # pyrefly: ignore # ty: ignore[invalid-method-override]
self,
year: int | None = ...,
month: int | None = ...,
day: int | None = ...,
hour: int | None = ...,
minute: int | None = ...,
second: int | None = ...,
microsecond: int | None = ...,
tzinfo: _tzinfo | None = ...,
fold: Literal[0, 1] | None = ...,
year: int | None = None,
month: int | None = None,
day: int | None = None,
hour: int | None = None,
minute: int | None = None,
second: int | None = None,
microsecond: int | None = None,
tzinfo: _tzinfo | None = None,
fold: Literal[0, 1] | None = None,
) -> Timestamp: ...
def astimezone(self, tz: _tzinfo | None = ...) -> Self: ...
def astimezone(self, tz: _tzinfo | None = None) -> Self: ...
def ctime(self) -> str: ...
def isoformat( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] # pyrefly: ignore[bad-override] # ty: ignore[invalid-method-override]
self,
Expand Down Expand Up @@ -261,9 +261,9 @@ class Timestamp(datetime, SupportsIndex):
def is_quarter_end(self) -> bool: ...
@property
def is_year_end(self) -> bool: ...
def to_pydatetime(self, warn: bool = ...) -> datetime: ...
def to_pydatetime(self, warn: bool = True) -> datetime: ...
def to_datetime64(self) -> np.datetime64: ...
def to_period(self, freq: PeriodFrequency | None = ...) -> Period: ...
def to_period(self, freq: PeriodFrequency | None = None) -> Period: ...
def to_julian_date(self) -> np.float64: ...
@property
def asm8(self) -> np.datetime64: ...
Expand Down
55 changes: 23 additions & 32 deletions pandas-stubs/core/arrays/timedeltas.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ from pandas._libs.tslibs.offsets import DateOffset
from pandas._libs.tslibs.timedeltas import Timedelta
from pandas._typing import (
AnyArrayLike,
AxisInt,
DtypeArg,
Frequency,
NpDtype,
Expand All @@ -34,37 +35,27 @@ class TimedeltaArray(TimelikeOps):
freq: Frequency | None = None,
copy: bool = ...,
) -> None: ...
# TODO: pandas-dev/pandas-stubs#1589 add testing to figure out the correct types
# def sum(
# self,
# *,
# axis=...,
# dtype=...,
# out=...,
# keepdims: bool = ...,
# initial=...,
# skipna: bool = ...,
# min_count: int = ...,
# ): ...
# def std(
# self,
# *,
# axis=...,
# dtype=...,
# out=...,
# ddof: int = ...,
# keepdims: bool = ...,
# skipna: bool = ...,
# ): ...
# def median(
# self,
# *,
# axis=...,
# out=...,
# overwrite_input: bool = ...,
# keepdims: bool = ...,
# skipna: bool = ...,
# ): ...
def sum(
self,
*,
axis: AxisInt | None = None,
dtype: NpDtype | None = None,
out: np_1darray_object | None = None,
keepdims: bool = False,
initial: object | None = None,
skipna: bool = True,
min_count: int = 0,
) -> Timedelta: ...
def std(
self,
*,
axis: AxisInt | None = None,
dtype: DtypeArg | None = None,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These reduction methods (sum, std, etc.) can return NaT (represented as NaTType) when skipna=False or on empty inputs. The return type should be expanded to Timedelta | NaTType.

Ground Truth: core/arrays/timedeltas.py#L367 (via _box_func)

Gemini 3 created, I verified

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't think we should return Timedelta | NaTType in this case because it will force users to do a cast when they know the result is valid. I'd be OK with an overload with skipna=False that returned Timedelta | NaTType

out: np_1darray_object | None = None,
ddof: int = 1,
keepdims: bool = False,
skipna: bool = True,
) -> Timedelta: ...
def __mul__(self, other: Any) -> Self: ...
__rmul__ = __mul__
@overload
Expand Down Expand Up @@ -123,7 +114,7 @@ class TimedeltaArray(TimelikeOps):
def min(self, *, skipna: bool = True, **kwargs: Any) -> Timedelta | NaTType: ...
def max(self, *, skipna: bool = True, **kwargs: Any) -> Timedelta | NaTType: ...
def mean(self, *, skipna: bool = True, **kwargs: Any) -> Timedelta | NaTType: ...
def median(self, *, skipna: bool = True, **kwargs: Any) -> Timedelta | NaTType: ...
def median(self, *, skipna: bool = True, **kwargs: Any) -> Timedelta: ...
def __array__(
self, dtype: NpDtype | None = None, copy: bool | None = None
) -> np_1darray_td: ...
Loading
Loading