diff --git a/pandas/_libs/tslibs/tzconversion.pyx b/pandas/_libs/tslibs/tzconversion.pyx index dffe02ef15148..2b7f9b9659354 100644 --- a/pandas/_libs/tslibs/tzconversion.pyx +++ b/pandas/_libs/tslibs/tzconversion.pyx @@ -519,7 +519,7 @@ cdef ndarray[int64_t] _get_dst_hours( trans_idx = mismatch.nonzero()[0] if trans_idx.size == 1: - # TODO: not reached in tests 2022-05-02; possible? + # see test_tz_localize_to_utc_ambiguous_infer stamp = _render_tstamp(vals[trans_idx[0]], reso=reso) raise pytz.AmbiguousTimeError( f"Cannot infer dst time from {stamp} as there " @@ -541,7 +541,7 @@ cdef ndarray[int64_t] _get_dst_hours( delta = np.diff(result_a[grp]) if grp.size == 1 or np.all(delta > 0): - # TODO: not reached in tests 2022-05-02; possible? + # see test_tz_localize_to_utc_ambiguous_infer stamp = _render_tstamp(vals[grp[0]], reso=reso) raise pytz.AmbiguousTimeError(stamp) @@ -549,7 +549,7 @@ cdef ndarray[int64_t] _get_dst_hours( # for standard switch_idxs = (delta <= 0).nonzero()[0] if switch_idxs.size > 1: - # TODO: not reached in tests 2022-05-02; possible? + # see test_tz_localize_to_utc_ambiguous_infer raise pytz.AmbiguousTimeError( f"There are {switch_idxs.size} dst switches when " "there should only be 1." diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 4b1d50c23c110..d577abd87bca3 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -1088,12 +1088,13 @@ def _cmp_method(self, other, op): @final def _add_datetimelike_scalar(self, other): - # Overridden by TimedeltaArray if not is_timedelta64_dtype(self.dtype): raise TypeError( f"cannot add {type(self).__name__} and {type(other).__name__}" ) + self = cast("TimedeltaArray", self) + from pandas.core.arrays import DatetimeArray assert other is not NaT @@ -1111,7 +1112,7 @@ def _add_datetimelike_scalar(self, other): return DatetimeArray(result, dtype=dtype, freq=self.freq) @final - def _add_datetime_arraylike(self, other): + def _add_datetime_arraylike(self, other) -> DatetimeArray: if not is_timedelta64_dtype(self.dtype): raise TypeError( f"cannot add {type(self).__name__} and {type(other).__name__}" @@ -1176,7 +1177,7 @@ def _sub_datetime_arraylike(self, other): return new_values.view("timedelta64[ns]") @final - def _sub_period(self, other: Period): + def _sub_period(self, other: Period) -> npt.NDArray[np.object_]: if not is_period_dtype(self.dtype): raise TypeError(f"cannot subtract Period from a {type(self).__name__}") @@ -1184,8 +1185,8 @@ def _sub_period(self, other: Period): # of DateOffsets. Null entries are filled with pd.NaT self._check_compatible_with(other) asi8 = self.asi8 - new_data = asi8 - other.ordinal - new_data = np.array([self.freq.base * x for x in new_data]) + new_i8_data = asi8 - other.ordinal # TODO: checked_add_with_arr + new_data = np.array([self.freq.base * x for x in new_i8_data]) if self._hasna: new_data[self._isnan] = NaT @@ -1193,7 +1194,7 @@ def _sub_period(self, other: Period): return new_data @final - def _add_period(self, other: Period): + def _add_period(self, other: Period) -> PeriodArray: if not is_timedelta64_dtype(self.dtype): raise TypeError(f"cannot add Period to a {type(self).__name__}") @@ -1683,12 +1684,11 @@ def median(self, *, axis: int | None = None, skipna: bool = True, **kwargs): return self._wrap_reduction_result(axis, result) def _mode(self, dropna: bool = True): - values = self + mask = None if dropna: - mask = values.isna() - values = values[~mask] + mask = self.isna() - i8modes = mode(values.view("i8")) + i8modes = mode(self.view("i8"), mask=mask) npmodes = i8modes.view(self._ndarray.dtype) npmodes = cast(np.ndarray, npmodes) return self._from_backing_data(npmodes) diff --git a/pandas/tests/tslibs/test_tzconversion.py b/pandas/tests/tslibs/test_tzconversion.py new file mode 100644 index 0000000000000..c1a56ffb71b02 --- /dev/null +++ b/pandas/tests/tslibs/test_tzconversion.py @@ -0,0 +1,23 @@ +import numpy as np +import pytest +import pytz + +from pandas._libs.tslibs.tzconversion import tz_localize_to_utc + + +class TestTZLocalizeToUTC: + def test_tz_localize_to_utc_ambiguous_infer(self): + # val is a timestamp that is ambiguous when localized to US/Eastern + val = 1_320_541_200_000_000_000 + vals = np.array([val, val - 1, val], dtype=np.int64) + + with pytest.raises(pytz.AmbiguousTimeError, match="2011-11-06 01:00:00"): + tz_localize_to_utc(vals, pytz.timezone("US/Eastern"), ambiguous="infer") + + with pytest.raises(pytz.AmbiguousTimeError, match="are no repeated times"): + tz_localize_to_utc(vals[:1], pytz.timezone("US/Eastern"), ambiguous="infer") + + vals[1] += 1 + msg = "There are 2 dst switches when there should only be 1" + with pytest.raises(pytz.AmbiguousTimeError, match=msg): + tz_localize_to_utc(vals, pytz.timezone("US/Eastern"), ambiguous="infer")