diff --git a/pandas/_libs/tslibs/timestamps.pyi b/pandas/_libs/tslibs/timestamps.pyi new file mode 100644 index 0000000000000..8728b700a1f6d --- /dev/null +++ b/pandas/_libs/tslibs/timestamps.pyi @@ -0,0 +1,205 @@ +from datetime import ( + date as _date, + datetime, + time as _time, + timedelta, + tzinfo as _tzinfo, +) +import sys +from time import struct_time +from typing import ( + ClassVar, + Optional, + Type, + TypeVar, + overload, +) + +import numpy as np + +from pandas._libs.tslibs import ( + NaT, + NaTType, + Period, + Timedelta, +) + +_S = TypeVar("_S") + + +def integer_op_not_supported(obj) -> None: ... + + +class Timestamp(datetime): + min: ClassVar[Timestamp] + max: ClassVar[Timestamp] + + resolution: ClassVar[Timedelta] + value: int # np.int64 + + # error: "__new__" must return a class instance (got "Union[Timestamp, NaTType]") + def __new__( # type: ignore[misc] + cls: Type[_S], + ts_input: int | np.integer | float | str | _date | datetime | np.datetime64 = ..., + freq=..., + tz: str | _tzinfo | None | int= ..., + unit=..., + year: int | None = ..., + month: int | None = ..., + day: int | None = ..., + hour: int | None = ..., + minute: int | None = ..., + second: int | None = ..., + microsecond: int | None = ..., + nanosecond: int | None = ..., + tzinfo: _tzinfo | None = ..., + *, + fold: int | None= ..., + ) -> _S | NaTType: ... + + @property + def year(self) -> int: ... + @property + def month(self) -> int: ... + @property + def day(self) -> int: ... + @property + def hour(self) -> int: ... + @property + def minute(self) -> int: ... + @property + def second(self) -> int: ... + @property + def microsecond(self) -> int: ... + @property + def tzinfo(self) -> Optional[_tzinfo]: ... + @property + def tz(self) -> Optional[_tzinfo]: ... + + @property + def fold(self) -> int: ... + + @classmethod + def fromtimestamp(cls: Type[_S], t: float, tz: Optional[_tzinfo] = ...) -> _S: ... + @classmethod + def utcfromtimestamp(cls: Type[_S], t: float) -> _S: ... + @classmethod + def today(cls: Type[_S]) -> _S: ... + @classmethod + def fromordinal(cls: Type[_S], n: int) -> _S: ... + + if sys.version_info >= (3, 8): + @classmethod + def now(cls: Type[_S], tz: _tzinfo | str | None = ...) -> _S: ... + else: + @overload + @classmethod + def now(cls: Type[_S], tz: None = ...) -> _S: ... + @overload + @classmethod + def now(cls, tz: _tzinfo) -> datetime: ... + + @classmethod + def utcnow(cls: Type[_S]) -> _S: ... + @classmethod + def combine(cls, date: _date, time: _time, tzinfo: Optional[_tzinfo] = ...) -> datetime: ... + + @classmethod + def fromisoformat(cls: Type[_S], date_string: str) -> _S: ... + + def strftime(self, fmt: str) -> str: ... + def __format__(self, fmt: str) -> str: ... + + def toordinal(self) -> int: ... + def timetuple(self) -> struct_time: ... + + def timestamp(self) -> float: ... + + def utctimetuple(self) -> struct_time: ... + def date(self) -> _date: ... + def time(self) -> _time: ... + def timetz(self) -> _time: ... + + def replace( + self, + year: int = ..., + month: int = ..., + day: int = ..., + hour: int = ..., + minute: int = ..., + second: int = ..., + microsecond: int = ..., + tzinfo: Optional[_tzinfo] = ..., + *, + fold: int = ..., + ) -> datetime: ... + + if sys.version_info >= (3, 8): + def astimezone(self: _S, tz: Optional[_tzinfo] = ...) -> _S: ... + else: + def astimezone(self, tz: Optional[_tzinfo] = ...) -> datetime: ... + + def ctime(self) -> str: ... + def isoformat(self, sep: str = ..., timespec: str = ...) -> str: ... + + @classmethod + def strptime(cls, date_string: str, format: str) -> datetime: ... + + def utcoffset(self) -> Optional[timedelta]: ... + def tzname(self) -> Optional[str]: ... + def dst(self) -> Optional[timedelta]: ... + + def __le__(self, other: datetime) -> bool: ... # type: ignore + def __lt__(self, other: datetime) -> bool: ... # type: ignore + def __ge__(self, other: datetime) -> bool: ... # type: ignore + def __gt__(self, other: datetime) -> bool: ... # type: ignore + if sys.version_info >= (3, 8): + def __add__(self: _S, other: timedelta) -> _S: ... + def __radd__(self: _S, other: timedelta) -> _S: ... + else: + def __add__(self, other: timedelta) -> datetime: ... + def __radd__(self, other: timedelta) -> datetime: ... + @overload # type: ignore + def __sub__(self, other: datetime) -> timedelta: ... + @overload + def __sub__(self, other: timedelta) -> datetime: ... + + def __hash__(self) -> int: ... + def weekday(self) -> int: ... + def isoweekday(self) -> int: ... + def isocalendar(self) -> tuple[int, int, int]: ... + + @property + def is_leap_year(self) -> bool: ... + @property + def is_month_start(self) -> bool: ... + @property + def is_quarter_start(self) -> bool: ... + @property + def is_year_start(self) -> bool: ... + @property + def is_month_end(self) -> bool: ... + @property + def is_quarter_end(self) -> bool: ... + @property + def is_year_end(self) -> bool: ... + + def to_pydatetime(self, warn: bool = ...) -> datetime: ... + def to_datetime64(self) -> np.datetime64: ... + def to_period(self, freq) -> Period: ... + def to_julian_date(self) -> np.float64: ... + + @property + def asm8(self) -> np.datetime64: ... + + def tz_convert(self: _S, tz) -> _S: ... + + # TODO: could return NaT? + def tz_localize(self: _S, tz, ambiguous: str = ..., nonexistent: str = ...) -> _S: ... + + def normalize(self: _S) -> _S: ... + + # TODO: round/floor/ceil could return NaT? + def round(self: _S, freq, ambiguous: bool | str = ..., nonexistent: str = ...) -> _S: ... + def floor(self: _S, freq, ambiguous: bool | str = ..., nonexistent: str = ...) -> _S: ... + def ceil(self: _S, freq, ambiguous: bool | str = ..., nonexistent: str = ...) -> _S: ... diff --git a/pandas/core/arrays/_ranges.py b/pandas/core/arrays/_ranges.py index 34d5ea6cfb20d..a537951786646 100644 --- a/pandas/core/arrays/_ranges.py +++ b/pandas/core/arrays/_ranges.py @@ -41,20 +41,20 @@ def generate_regular_range( ------- ndarray[np.int64] Representing nanoseconds. """ - start = start.value if start is not None else None - end = end.value if end is not None else None + istart = start.value if start is not None else None + iend = end.value if end is not None else None stride = freq.nanos if periods is None: - b = start + b = istart # cannot just use e = Timestamp(end) + 1 because arange breaks when # stride is too large, see GH10887 - e = b + (end - b) // stride * stride + stride // 2 + 1 - elif start is not None: - b = start + e = b + (iend - b) // stride * stride + stride // 2 + 1 + elif istart is not None: + b = istart e = _generate_range_overflow_safe(b, periods, stride, side="start") - elif end is not None: - e = end + stride + elif iend is not None: + e = iend + stride b = _generate_range_overflow_safe(e, periods, stride, side="end") else: raise ValueError( diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index 289ed4948934f..117b267fd49e5 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -742,7 +742,9 @@ def _sub_datetimelike_scalar(self, other): assert isinstance(other, (datetime, np.datetime64)) assert other is not NaT other = Timestamp(other) - if other is NaT: + # error: Non-overlapping identity check (left operand type: "Timestamp", + # right operand type: "NaTType") + if other is NaT: # type: ignore[comparison-overlap] return self - NaT if not self._has_same_tz(other): diff --git a/pandas/core/dtypes/cast.py b/pandas/core/dtypes/cast.py index d739b46620032..d4ecec667cc86 100644 --- a/pandas/core/dtypes/cast.py +++ b/pandas/core/dtypes/cast.py @@ -189,13 +189,13 @@ def maybe_box_native(value: Scalar) -> Scalar: value = maybe_box_datetimelike(value) elif is_float(value): # error: Argument 1 to "float" has incompatible type - # "Union[Union[str, int, float, bool], Union[Any, Any, Timedelta, Any]]"; + # "Union[Union[str, int, float, bool], Union[Any, Timestamp, Timedelta, Any]]"; # expected "Union[SupportsFloat, _SupportsIndex, str]" value = float(value) # type: ignore[arg-type] elif is_integer(value): # error: Argument 1 to "int" has incompatible type - # "Union[Union[str, int, float, bool], Union[Any, Any, Timedelta, Any]]"; - # pected "Union[str, SupportsInt, _SupportsIndex, _SupportsTrunc]" + # "Union[Union[str, int, float, bool], Union[Any, Timestamp, Timedelta, Any]]"; + # expected "Union[str, SupportsInt, _SupportsIndex, _SupportsTrunc]" value = int(value) # type: ignore[arg-type] elif is_bool(value): value = bool(value) @@ -729,7 +729,9 @@ def infer_dtype_from_scalar(val, pandas_dtype: bool = False) -> tuple[DtypeObj, except OutOfBoundsDatetime: return np.dtype(object), val - if val is NaT or val.tz is None: + # error: Non-overlapping identity check (left operand type: "Timestamp", + # right operand type: "NaTType") + if val is NaT or val.tz is None: # type: ignore[comparison-overlap] dtype = np.dtype("M8[ns]") val = val.to_datetime64() else: @@ -2056,7 +2058,7 @@ def validate_numeric_casting(dtype: np.dtype, value: Scalar) -> None: ValueError """ # error: Argument 1 to "__call__" of "ufunc" has incompatible type - # "Union[Union[str, int, float, bool], Union[Any, Any, Timedelta, Any]]"; + # "Union[Union[str, int, float, bool], Union[Any, Timestamp, Timedelta, Any]]"; # expected "Union[Union[int, float, complex, str, bytes, generic], # Sequence[Union[int, float, complex, str, bytes, generic]], # Sequence[Sequence[Any]], _SupportsArray]" diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 2d7d83d6a2bc3..61396fdf372d5 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -1892,7 +1892,9 @@ def get_block_type(values, dtype: Dtype | None = None): cls = ExtensionBlock elif isinstance(dtype, CategoricalDtype): cls = CategoricalBlock - elif vtype is Timestamp: + # error: Non-overlapping identity check (left operand type: "Type[generic]", + # right operand type: "Type[Timestamp]") + elif vtype is Timestamp: # type: ignore[comparison-overlap] cls = DatetimeTZBlock elif isinstance(dtype, ExtensionDtype): # Note: need to be sure PandasArray is unwrapped before we get here diff --git a/pandas/core/tools/datetimes.py b/pandas/core/tools/datetimes.py index bb37f670ed302..8577bb5dc311b 100644 --- a/pandas/core/tools/datetimes.py +++ b/pandas/core/tools/datetimes.py @@ -628,16 +628,16 @@ def _adjust_to_origin(arg, origin, unit): if offset.tz is not None: raise ValueError(f"origin offset {offset} must be tz-naive") - offset -= Timestamp(0) + td_offset = offset - Timestamp(0) # convert the offset to the unit of the arg # this should be lossless in terms of precision - offset = offset // Timedelta(1, unit=unit) + ioffset = td_offset // Timedelta(1, unit=unit) # scalars & ndarray-like can handle the addition if is_list_like(arg) and not isinstance(arg, (ABCSeries, Index, np.ndarray)): arg = np.asarray(arg) - arg = arg + offset + arg = arg + ioffset return arg @@ -887,13 +887,17 @@ def to_datetime( infer_datetime_format=infer_datetime_format, ) + result: Timestamp | NaTType | Series | Index + if isinstance(arg, Timestamp): result = arg if tz is not None: if arg.tz is not None: - result = result.tz_convert(tz) + # error: Too many arguments for "tz_convert" of "NaTType" + result = result.tz_convert(tz) # type: ignore[call-arg] else: - result = result.tz_localize(tz) + # error: Too many arguments for "tz_localize" of "NaTType" + result = result.tz_localize(tz) # type: ignore[call-arg] elif isinstance(arg, ABCSeries): cache_array = _maybe_cache(arg, format, cache, convert_listlike) if not cache_array.empty: @@ -928,7 +932,10 @@ def to_datetime( else: result = convert_listlike(np.array([arg]), format)[0] - return result + # error: Incompatible return value type (got "Union[Timestamp, NaTType, + # Series, Index]", expected "Union[DatetimeIndex, Series, float, str, + # NaTType, None]") + return result # type: ignore[return-value] # mappings for assembling units diff --git a/pandas/io/excel/_odfreader.py b/pandas/io/excel/_odfreader.py index 1324485f49bdb..c105465cddd95 100644 --- a/pandas/io/excel/_odfreader.py +++ b/pandas/io/excel/_odfreader.py @@ -1,7 +1,4 @@ -from typing import ( - List, - cast, -) +from typing import List import numpy as np @@ -200,10 +197,9 @@ def _get_cell_value(self, cell, convert_float: bool) -> Scalar: cell_value = cell.attributes.get((OFFICENS, "date-value")) return pd.to_datetime(cell_value) elif cell_type == "time": - result = pd.to_datetime(str(cell)) - result = cast(pd.Timestamp, result) + stamp = pd.to_datetime(str(cell)) # error: Item "str" of "Union[float, str, NaTType]" has no attribute "time" - return result.time() # type: ignore[union-attr] + return stamp.time() # type: ignore[union-attr] else: self.close() raise ValueError(f"Unrecognized type {cell_type}") diff --git a/pandas/tests/indexes/test_engines.py b/pandas/tests/indexes/test_engines.py index 52af29d999fcc..9f41c68909f6e 100644 --- a/pandas/tests/indexes/test_engines.py +++ b/pandas/tests/indexes/test_engines.py @@ -61,7 +61,13 @@ class TestTimedeltaEngine: @pytest.mark.parametrize( "scalar", [ - pd.Timestamp(pd.Timedelta(days=42).asm8.view("datetime64[ns]")), + # error: Argument 1 to "Timestamp" has incompatible type "timedelta64"; + # expected "Union[integer[Any], float, str, date, datetime64]" + pd.Timestamp( + pd.Timedelta(days=42).asm8.view( + "datetime64[ns]" + ) # type: ignore[arg-type] + ), pd.Timedelta(days=42).value, pd.Timedelta(days=42).to_pytimedelta(), pd.Timedelta(days=42).to_timedelta64(), diff --git a/pandas/tests/scalar/timestamp/test_constructors.py b/pandas/tests/scalar/timestamp/test_constructors.py index 663892cefb5e6..98ec4de614a07 100644 --- a/pandas/tests/scalar/timestamp/test_constructors.py +++ b/pandas/tests/scalar/timestamp/test_constructors.py @@ -331,7 +331,9 @@ def test_constructor_fromordinal(self): tz="UTC", ), Timestamp(2000, 1, 2, 3, 4, 5, 6, 1, None), - Timestamp(2000, 1, 2, 3, 4, 5, 6, 1, pytz.UTC), + # error: Argument 9 to "Timestamp" has incompatible type "_UTCclass"; + # expected "Optional[int]" + Timestamp(2000, 1, 2, 3, 4, 5, 6, 1, pytz.UTC), # type: ignore[arg-type] ], ) def test_constructor_nanosecond(self, result):