Repository: pandas-dev/pandas

Issue:
DEPR: Timedelta with passed unit M, Y
```
In [7]: pd.Timedelta(1, unit='Y')
Out[7]: Timedelta('365 days 05:49:12')

In [8]: pd.Timedelta(1, unit='M')
Out[8]: Timedelta('30 days 10:29:06')
```

The above does not make much sense, so lets deprecate this (similar for `to_timedelta`).

For the case where those unit abbreviations are used in a string, `Y` already raises, while `M` is actually interpreted as minutes:

```In [10]: pd.to_timedelta('1Y')
ValueError: invalid abbreviation: Y

In [13]: pd.to_timedelta('1M')
Out[13]: Timedelta('0 days 00:01:00')
```


xref https://github.com/pandas-dev/pandas/issues/16335, https://github.com/pandas-dev/pandas/issues/12691 and https://github.com/pandas-dev/pandas/issues/14024


Discussion:
One question is whether we should also deprecate 'W' for week (see https://github.com/pandas-dev/pandas/issues/14024#issuecomment-240680919).

Given that this can be easily represented as 7 days, it would be easier/clearer to just have 'D' as the highest allowed unit. 
On the other hand, 'W' currently gives a sensible output (as opposed to M/Y), so less reason to remove this behaviour:

```
In [15]: pd.to_timedelta(1, unit='W')
Out[15]: Timedelta('7 days 00:00:00')
```

Resolving patch:
diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py
--- 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
--- 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):


Test patch:
diff --git a/pandas/tests/indexes/timedeltas/test_timedelta.py b/pandas/tests/indexes/timedeltas/test_timedelta.py
--- 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
--- 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')