diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index f7faeea7a646f..2b1a61186dca6 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -502,7 +502,7 @@ Other Deprecations Use the public attributes :attr:`~RangeIndex.start`, :attr:`~RangeIndex.stop` and :attr:`~RangeIndex.step` instead (:issue:`26581`). - The :meth:`Series.ftype`, :meth:`Series.ftypes` and :meth:`DataFrame.ftypes` methods are deprecated and will be removed in a future version. Instead, use :meth:`Series.dtype` and :meth:`DataFrame.dtypes` (:issue:`26705`). - +- :meth:`Timedelta.resolution` is deprecated and replaced with :meth:`Timedelta.resolution_string`. In a future version, :meth:`Timedelta.resolution` will be changed to behave like the standard library :attr:`timedelta.resolution` (:issue:`21344`) .. _whatsnew_0250.prior_deprecations: diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index ad60165e98d4f..6a32553fe2d38 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -950,7 +950,7 @@ cdef class _Timedelta(timedelta): return np.int64(self.value).view('m8[ns]') @property - def resolution(self): + def resolution_string(self): """ Return a string representing the lowest timedelta resolution. @@ -991,7 +991,6 @@ cdef class _Timedelta(timedelta): >>> td.resolution 'U' """ - self._ensure_components() if self._ns: return "N" @@ -1008,6 +1007,56 @@ cdef class _Timedelta(timedelta): else: return "D" + @property + def resolution(self): + """ + Return a string representing the lowest timedelta resolution. + + Each timedelta has a defined resolution that represents the lowest OR + most granular level of precision. Each level of resolution is + represented by a short string as defined below: + + Resolution: Return value + + * Days: 'D' + * Hours: 'H' + * Minutes: 'T' + * Seconds: 'S' + * Milliseconds: 'L' + * Microseconds: 'U' + * Nanoseconds: 'N' + + Returns + ------- + str + Timedelta resolution. + + Examples + -------- + >>> td = pd.Timedelta('1 days 2 min 3 us 42 ns') + >>> td.resolution + 'N' + + >>> td = pd.Timedelta('1 days 2 min 3 us') + >>> td.resolution + 'U' + + >>> td = pd.Timedelta('2 min 3 s') + >>> td.resolution + 'S' + + >>> td = pd.Timedelta(36, unit='us') + >>> td.resolution + 'U' + """ + # See GH#21344 + warnings.warn("Timedelta.resolution is deprecated, in a future " + "version will behave like the standard library " + "datetime.timedelta.resolution attribute. " + "Use Timedelta.resolution_string instead.", + FutureWarning) + return self.resolution_string + @property def nanoseconds(self): """ diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index f5362c0b6bb5d..ba5507fa71e8c 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -551,11 +551,11 @@ def _maybe_cast_slice_bound(self, label, side, kind): if isinstance(label, str): parsed = Timedelta(label) - lbound = parsed.round(parsed.resolution) + lbound = parsed.round(parsed.resolution_string) if side == 'left': return lbound else: - return (lbound + to_offset(parsed.resolution) - + return (lbound + to_offset(parsed.resolution_string) - Timedelta(1, 'ns')) elif ((is_integer(label) or is_float(label)) and not is_timedelta64_dtype(label)): diff --git a/pandas/tests/scalar/test_nat.py b/pandas/tests/scalar/test_nat.py index 0ae4d107d85bd..19426c3bf3ffb 100644 --- a/pandas/tests/scalar/test_nat.py +++ b/pandas/tests/scalar/test_nat.py @@ -142,8 +142,8 @@ def test_nat_iso_format(get_nat): @pytest.mark.parametrize("klass,expected", [ (Timestamp, ["freqstr", "normalize", "to_julian_date", "to_period", "tz"]), - (Timedelta, ["components", "delta", "is_populated", "to_pytimedelta", - "to_timedelta64", "view"]) + (Timedelta, ["components", "delta", "is_populated", "resolution_string", + "to_pytimedelta", "to_timedelta64", "view"]) ]) def test_missing_public_nat_methods(klass, expected): # see gh-17327 diff --git a/pandas/tests/scalar/timedelta/test_timedelta.py b/pandas/tests/scalar/timedelta/test_timedelta.py index 57b3705640202..f10876531e66a 100644 --- a/pandas/tests/scalar/timedelta/test_timedelta.py +++ b/pandas/tests/scalar/timedelta/test_timedelta.py @@ -742,6 +742,22 @@ def test_components(self): assert not result.iloc[0].isna().all() assert result.iloc[1].isna().all() + def test_resolution_string(self): + assert Timedelta(days=1).resolution_string == 'D' + assert Timedelta(days=1, hours=6).resolution_string == 'H' + assert Timedelta(days=1, minutes=6).resolution_string == 'T' + assert Timedelta(days=1, seconds=6).resolution_string == 'S' + assert Timedelta(days=1, milliseconds=6).resolution_string == 'L' + assert Timedelta(days=1, microseconds=6).resolution_string == 'U' + assert Timedelta(days=1, nanoseconds=6).resolution_string == 'N' + + def test_resolution_deprecated(self): + # GH#21344 + td = Timedelta(days=4, hours=3) + with tm.assert_produces_warning(FutureWarning) as w: + td.resolution + assert "Use Timedelta.resolution_string instead" in str(w[0].message) + @pytest.mark.parametrize('value, expected', [ (Timedelta('10S'), True),