diff --git a/pandas/_typing.py b/pandas/_typing.py index 71df27119bd96..4892abc5f6f51 100644 --- a/pandas/_typing.py +++ b/pandas/_typing.py @@ -1,4 +1,4 @@ -from datetime import datetime, timedelta +from datetime import datetime, timedelta, tzinfo from pathlib import Path from typing import ( IO, @@ -52,6 +52,7 @@ TimedeltaConvertibleTypes = Union[ "Timedelta", timedelta, np.timedelta64, int, np.int64, float, str ] +Timezone = Union[str, tzinfo] # other diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index e1f0221eaee65..85625a8aea523 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -10,7 +10,7 @@ from pandas._libs.tslibs.frequencies import get_freq_group from pandas._libs.tslibs.offsets import prefix_mapping from pandas._typing import DtypeObj, Label -from pandas.util._decorators import cache_readonly +from pandas.util._decorators import cache_readonly, doc from pandas.core.dtypes.common import ( DT64NS_DTYPE, @@ -64,9 +64,13 @@ def _new_DatetimeIndex(cls, d): @inherit_names( - ["to_period", "to_perioddelta", "to_julian_date", "strftime", "isocalendar"] + ["to_perioddelta", "to_julian_date", "strftime", "isocalendar"] + DatetimeArray._field_ops - + DatetimeArray._datetimelike_methods, + + [ + method + for method in DatetimeArray._datetimelike_methods + if method not in ("tz_localize",) + ], DatetimeArray, wrap=True, ) @@ -218,6 +222,21 @@ class DatetimeIndex(DatetimeTimedeltaMixin): _data: DatetimeArray tz: Optional[tzinfo] + # -------------------------------------------------------------------- + # methods that dispatch to array and wrap result in DatetimeIndex + + @doc(DatetimeArray.tz_localize) + def tz_localize( + self, tz, ambiguous="raise", nonexistent="raise" + ) -> "DatetimeIndex": + arr = self._data.tz_localize(tz, ambiguous, nonexistent) + return type(self)._simple_new(arr, name=self.name) + + @doc(DatetimeArray.to_period) + def to_period(self, freq=None) -> "DatetimeIndex": + arr = self._data.to_period(freq) + return type(self)._simple_new(arr, name=self.name) + # -------------------------------------------------------------------- # Constructors diff --git a/pandas/core/series.py b/pandas/core/series.py index 71ffdcbd40fe7..b51c08fa592d5 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -4846,7 +4846,7 @@ def to_period(self, freq=None, copy=True) -> "Series": if not isinstance(self.index, DatetimeIndex): raise TypeError(f"unsupported Type {type(self.index).__name__}") - new_index = self.index.to_period(freq=freq) # type: ignore + new_index = self.index.to_period(freq=freq) return self._constructor(new_values, index=new_index).__finalize__( self, method="to_period" ) diff --git a/pandas/core/tools/datetimes.py b/pandas/core/tools/datetimes.py index 42bffa0374472..0adab143f6052 100644 --- a/pandas/core/tools/datetimes.py +++ b/pandas/core/tools/datetimes.py @@ -2,7 +2,16 @@ from datetime import datetime from functools import partial from itertools import islice -from typing import TYPE_CHECKING, Optional, TypeVar, Union +from typing import ( + TYPE_CHECKING, + Callable, + List, + Optional, + Tuple, + TypeVar, + Union, + overload, +) import warnings import numpy as np @@ -15,7 +24,7 @@ _guess_datetime_format, ) from pandas._libs.tslibs.strptime import array_strptime -from pandas._typing import ArrayLike +from pandas._typing import ArrayLike, Label, Timezone from pandas.core.dtypes.common import ( ensure_object, @@ -45,16 +54,15 @@ if TYPE_CHECKING: from pandas import Series # noqa:F401 + from pandas._libs.tslibs.nattype import NaTType # noqa:F401 # --------------------------------------------------------------------- # types used in annotations -ArrayConvertible = Union[list, tuple, ArrayLike, "Series"] +ArrayConvertible = Union[List, Tuple, ArrayLike, "Series"] Scalar = Union[int, float, str] DatetimeScalar = TypeVar("DatetimeScalar", Scalar, datetime) -DatetimeScalarOrArrayConvertible = Union[ - DatetimeScalar, list, tuple, ArrayLike, "Series" -] +DatetimeScalarOrArrayConvertible = Union[DatetimeScalar, ArrayConvertible] # --------------------------------------------------------------------- @@ -123,7 +131,12 @@ def should_cache( return do_caching -def _maybe_cache(arg, format, cache, convert_listlike): +def _maybe_cache( + arg: ArrayConvertible, + format: Optional[str], + cache: bool, + convert_listlike: Callable, +) -> "Series": """ Create a cache of unique dates from an array of dates @@ -159,7 +172,7 @@ def _maybe_cache(arg, format, cache, convert_listlike): def _box_as_indexlike( - dt_array: ArrayLike, utc: Optional[bool] = None, name: Optional[str] = None + dt_array: ArrayLike, utc: Optional[bool] = None, name: Label = None ) -> Index: """ Properly boxes the ndarray of datetimes to DatetimeIndex @@ -244,15 +257,15 @@ def _return_parsed_timezone_results(result, timezones, tz, name): def _convert_listlike_datetimes( arg, - format, - name=None, - tz=None, - unit=None, - errors=None, - infer_datetime_format=None, - dayfirst=None, - yearfirst=None, - exact=None, + format: Optional[str], + name: Label = None, + tz: Optional[Timezone] = None, + unit: Optional[str] = None, + errors: Optional[str] = None, + infer_datetime_format: Optional[bool] = None, + dayfirst: Optional[bool] = None, + yearfirst: Optional[bool] = None, + exact: Optional[bool] = None, ): """ Helper function for to_datetime. Performs the conversions of 1D listlike @@ -306,9 +319,7 @@ def _convert_listlike_datetimes( pass elif tz: # DatetimeArray, DatetimeIndex - # error: Item "DatetimeIndex" of "Union[DatetimeArray, DatetimeIndex]" has - # no attribute "tz_localize" - return arg.tz_localize(tz) # type: ignore + return arg.tz_localize(tz) return arg @@ -539,19 +550,70 @@ def _adjust_to_origin(arg, origin, unit): return arg +@overload def to_datetime( - arg, - errors="raise", - dayfirst=False, - yearfirst=False, - utc=None, - format=None, - exact=True, - unit=None, - infer_datetime_format=False, + arg: DatetimeScalar, + errors: str = ..., + dayfirst: bool = ..., + yearfirst: bool = ..., + utc: Optional[bool] = ..., + format: Optional[str] = ..., + exact: bool = ..., + unit: Optional[str] = ..., + infer_datetime_format: bool = ..., + origin=..., + cache: bool = ..., +) -> Union[DatetimeScalar, "NaTType"]: + ... + + +@overload +def to_datetime( + arg: "Series", + errors: str = ..., + dayfirst: bool = ..., + yearfirst: bool = ..., + utc: Optional[bool] = ..., + format: Optional[str] = ..., + exact: bool = ..., + unit: Optional[str] = ..., + infer_datetime_format: bool = ..., + origin=..., + cache: bool = ..., +) -> "Series": + ... + + +@overload +def to_datetime( + arg: Union[List, Tuple], + errors: str = ..., + dayfirst: bool = ..., + yearfirst: bool = ..., + utc: Optional[bool] = ..., + format: Optional[str] = ..., + exact: bool = ..., + unit: Optional[str] = ..., + infer_datetime_format: bool = ..., + origin=..., + cache: bool = ..., +) -> DatetimeIndex: + ... + + +def to_datetime( + arg: DatetimeScalarOrArrayConvertible, + errors: str = "raise", + dayfirst: bool = False, + yearfirst: bool = False, + utc: Optional[bool] = None, + format: Optional[str] = None, + exact: bool = True, + unit: Optional[str] = None, + infer_datetime_format: bool = False, origin="unix", - cache=True, -): + cache: bool = True, +) -> Union[DatetimeIndex, "Series", DatetimeScalar, "NaTType"]: """ Convert argument to datetime. @@ -746,8 +808,7 @@ def to_datetime( if not cache_array.empty: result = _convert_and_box_cache(arg, cache_array, name=arg.name) else: - convert_listlike = partial(convert_listlike, name=arg.name) - result = convert_listlike(arg, format) + result = convert_listlike(arg, format, name=arg.name) elif is_list_like(arg): try: cache_array = _maybe_cache(arg, format, cache, convert_listlike) diff --git a/pandas/io/excel/_odfreader.py b/pandas/io/excel/_odfreader.py index 739c77d1c0b99..be86b57ca2066 100644 --- a/pandas/io/excel/_odfreader.py +++ b/pandas/io/excel/_odfreader.py @@ -1,4 +1,4 @@ -from typing import List +from typing import List, cast from pandas._typing import FilePathOrBuffer, Scalar from pandas.compat._optional import import_optional_dependency @@ -179,7 +179,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": - return pd.to_datetime(str(cell)).time() + result = pd.to_datetime(str(cell)) + result = cast(pd.Timestamp, result) + return result.time() else: raise ValueError(f"Unrecognized type {cell_type}")