diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 6b6010fa9c605..4032dc20b2e19 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -42,10 +42,7 @@ Other API Changes Deprecations ~~~~~~~~~~~~ -- -- -- - +- Deprecated the `M (months)` and `Y (year)` `units` parameter of :func: `pandas.to_timedelta`, :func: `pandas.Timedelta` and :func: `pandas.TimedeltaIndex` (:issue:`16344`) .. _whatsnew_0250.prior_deprecations: diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index 0a19d8749fc7c..f08a57375a301 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -1158,6 +1158,11 @@ class Timedelta(_Timedelta): "[weeks, days, hours, minutes, seconds, " "milliseconds, microseconds, nanoseconds]") + if unit in {'Y', 'y', 'M'}: + warnings.warn("M and Y units are deprecated and " + "will be removed in a future version.", + FutureWarning, stacklevel=1) + if isinstance(value, Timedelta): value = value.value elif is_string_object(value): diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index cbe5ae198838f..830925535dab1 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -207,6 +207,11 @@ def __new__(cls, data=None, unit=None, freq=None, start=None, end=None, 'collection of some kind, {data} was passed' .format(cls=cls.__name__, data=repr(data))) + if unit in {'Y', 'y', 'M'}: + warnings.warn("M and Y units are deprecated and " + "will be removed in a future version.", + FutureWarning, stacklevel=2) + if isinstance(data, TimedeltaArray): if copy: data = data.copy() diff --git a/pandas/core/tools/timedeltas.py b/pandas/core/tools/timedeltas.py index ddd21d0f62d08..30cb15f311b9f 100644 --- a/pandas/core/tools/timedeltas.py +++ b/pandas/core/tools/timedeltas.py @@ -2,6 +2,8 @@ timedelta support tools """ +import warnings + import numpy as np from pandas._libs.tslibs.timedeltas import Timedelta, parse_timedelta_unit @@ -90,6 +92,11 @@ def to_timedelta(arg, unit='ns', box=True, errors='raise'): raise ValueError("errors must be one of 'ignore', " "'raise', or 'coerce'}") + if unit in {'Y', 'y', 'M'}: + warnings.warn("M and Y units are deprecated and " + "will be removed in a future version.", + FutureWarning, stacklevel=2) + if arg is None: return arg elif isinstance(arg, ABCSeries): diff --git a/pandas/tests/indexes/timedeltas/test_timedelta.py b/pandas/tests/indexes/timedeltas/test_timedelta.py index 79210705103ab..3cbd9942f9d84 100644 --- a/pandas/tests/indexes/timedeltas/test_timedelta.py +++ b/pandas/tests/indexes/timedeltas/test_timedelta.py @@ -1,4 +1,5 @@ from datetime import timedelta +import re import numpy as np import pytest @@ -325,6 +326,13 @@ def test_freq_conversion(self): result = td.astype('timedelta64[s]') assert_index_equal(result, expected) + @pytest.mark.parametrize('unit', ['Y', 'y', 'M']) + def test_unit_m_y_deprecated(self, unit): + with tm.assert_produces_warning(FutureWarning) as w: + TimedeltaIndex([1, 3, 7], unit) + msg = r'.* units are deprecated .*' + assert re.match(msg, str(w[0].message)) + class TestTimeSeries(object): diff --git a/pandas/tests/scalar/timedelta/test_timedelta.py b/pandas/tests/scalar/timedelta/test_timedelta.py index e1838e0160fec..7d5b479810205 100644 --- a/pandas/tests/scalar/timedelta/test_timedelta.py +++ b/pandas/tests/scalar/timedelta/test_timedelta.py @@ -1,5 +1,6 @@ """ test the scalar Timedelta """ from datetime import timedelta +import re import numpy as np import pytest @@ -317,6 +318,7 @@ def test_nat_converters(self): assert result.dtype.kind == 'm' assert result.astype('int64') == iNaT + @pytest.mark.filterwarnings("ignore:M and Y units are deprecated") @pytest.mark.parametrize('units, np_unit', [(['Y', 'y'], 'Y'), (['M'], 'M'), @@ -376,6 +378,24 @@ def test_unit_parser(self, units, np_unit, wrapper): result = Timedelta('2{}'.format(unit)) assert result == expected + @pytest.mark.skipif(compat.PY2, reason="requires python3.5 or higher") + @pytest.mark.parametrize('unit', ['Y', 'y', 'M']) + def test_unit_m_y_deprecated(self, unit): + with tm.assert_produces_warning(FutureWarning) as w1: + Timedelta(10, unit) + msg = r'.* units are deprecated .*' + assert re.match(msg, str(w1[0].message)) + with tm.assert_produces_warning(FutureWarning, + check_stacklevel=False) as w2: + to_timedelta(10, unit) + msg = r'.* units are deprecated .*' + assert re.match(msg, str(w2[0].message)) + with tm.assert_produces_warning(FutureWarning, + check_stacklevel=False) as w3: + to_timedelta([1, 2], unit) + msg = r'.* units are deprecated .*' + assert re.match(msg, str(w3[0].message)) + def test_numeric_conversions(self): assert Timedelta(0) == np.timedelta64(0, 'ns') assert Timedelta(10) == np.timedelta64(10, 'ns')