Repository: pandas-dev/pandas

Issue:
pd.Timedelta() cannot process offset alias T while pd.infer_freq() return this format.
#### Code Sample, a copy-pastable example if possible

```python
import pandas as pd
series = pd.Series([1, 2, 3], pd.date_range('201701010000', '201701010030', freq='15min'))
print(pd.infer_freq(series.index))
# Error
pd.Timedelta(pd.infer_freq(series.index))
# While this works
pd.Timedelta(series.index.freq)
```
#### Problem description

pd.Timedelta constructor works with e.g. '15min' but not with '15T',
while pd.infer_freq() returns '15T' and pd.Series.index.freq returns '15min'.
This leads to errors.

#### Expected Output

Same behaviour of pd.Timedelta() for 'min' and 'T' offset alias or deprecation of 'T' as an offset alias.

#### Output of ``pd.show_versions()``

<details>
INSTALLED VERSIONS
------------------
commit: None
python: 3.6.4.final.0
python-bits: 64
OS: Windows
OS-release: 10
machine: AMD64
processor: Intel64 Family 6 Model 142 Stepping 9, GenuineIntel
byteorder: little
LC_ALL: None
LANG: None
LOCALE: None.None
pandas: 0.23.1
pytest: 3.6.2
pip: 10.0.1
setuptools: 39.2.0
Cython: None
numpy: 1.14.5
scipy: 1.1.0
pyarrow: None
xarray: None
IPython: 6.4.0
sphinx: None
patsy: None
dateutil: 2.7.3
pytz: 2018.5
blosc: None
bottleneck: None
tables: None
numexpr: None
feather: None
matplotlib: 2.2.2
openpyxl: None
xlrd: None
xlwt: None
xlsxwriter: None
lxml: None
bs4: None
html5lib: None
sqlalchemy: None
pymysql: None
psycopg2: None
jinja2: None
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: None

</details>



Discussion:
Weird!  My first inclination is to accept `T` as an offset.

cc @jreback 

Resolving patch:
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
@@ -6,7 +6,8 @@
 import pandas as pd
 from pandas._libs import tslibs
 from pandas._libs.tslibs.timedeltas import (convert_to_timedelta64,
-                                            array_to_timedelta64)
+                                            array_to_timedelta64,
+                                            parse_timedelta_unit)
 
 from pandas.core.dtypes.common import (
     ensure_object,
@@ -23,8 +24,14 @@ def to_timedelta(arg, unit='ns', box=True, errors='raise'):
     Parameters
     ----------
     arg : string, timedelta, list, tuple, 1-d array, or Series
-    unit : unit of the arg (D,h,m,s,ms,us,ns) denote the unit, which is an
-        integer/float number
+    unit : string, {'Y', 'M', 'W', 'D', 'days', 'day',
+                    'hours', hour', 'hr', 'h', 'm', 'minute', 'min', 'minutes',
+                    'T', 'S', 'seconds', 'sec', 'second', 'ms',
+                    'milliseconds', 'millisecond', 'milli', 'millis', 'L',
+                    'us', 'microseconds', 'microsecond', 'micro', 'micros',
+                    'U', 'ns', 'nanoseconds', 'nano', 'nanos', 'nanosecond'
+                    'N'}, optional
+        Denote the unit of the input, if input is an integer. Default 'ns'.
     box : boolean, default True
         - If True returns a Timedelta/TimedeltaIndex of the results
         - if False returns a np.timedelta64 or ndarray of values of dtype
@@ -69,7 +76,7 @@ def to_timedelta(arg, unit='ns', box=True, errors='raise'):
     pandas.DataFrame.astype : Cast argument to a specified dtype.
     pandas.to_datetime : Convert argument to datetime.
     """
-    unit = _validate_timedelta_unit(unit)
+    unit = parse_timedelta_unit(unit)
 
     if errors not in ('ignore', 'raise', 'coerce'):
         raise ValueError("errors must be one of 'ignore', "
@@ -99,45 +106,6 @@ def to_timedelta(arg, unit='ns', box=True, errors='raise'):
                                             box=box, errors=errors)
 
 
-_unit_map = {
-    'Y': 'Y',
-    'y': 'Y',
-    'W': 'W',
-    'w': 'W',
-    'D': 'D',
-    'd': 'D',
-    'days': 'D',
-    'Days': 'D',
-    'day': 'D',
-    'Day': 'D',
-    'M': 'M',
-    'H': 'h',
-    'h': 'h',
-    'm': 'm',
-    'T': 'm',
-    'S': 's',
-    's': 's',
-    'L': 'ms',
-    'MS': 'ms',
-    'ms': 'ms',
-    'US': 'us',
-    'us': 'us',
-    'NS': 'ns',
-    'ns': 'ns',
-}
-
-
-def _validate_timedelta_unit(arg):
-    """ provide validation / translation for timedelta short units """
-    try:
-        return _unit_map[arg]
-    except (KeyError, TypeError):
-        if arg is None:
-            return 'ns'
-        raise ValueError("invalid timedelta unit {arg} provided"
-                         .format(arg=arg))
-
-
 def _coerce_scalar_to_timedelta_type(r, unit='ns', box=True, errors='raise'):
     """Convert string 'r' to a timedelta object."""
 


Test patch:
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
@@ -293,37 +293,64 @@ def test_nat_converters(self):
         assert to_timedelta('nat', box=False).astype('int64') == iNaT
         assert to_timedelta('nan', box=False).astype('int64') == iNaT
 
-        def testit(unit, transform):
-
-            # array
-            result = to_timedelta(np.arange(5), unit=unit)
-            expected = TimedeltaIndex([np.timedelta64(i, transform(unit))
+    @pytest.mark.parametrize('units, np_unit',
+                             [(['Y', 'y'], 'Y'),
+                              (['M'], 'M'),
+                              (['W', 'w'], 'W'),
+                              (['D', 'd', 'days', 'day', 'Days', 'Day'], 'D'),
+                              (['m', 'minute', 'min', 'minutes', 't',
+                                'Minute', 'Min', 'Minutes', 'T'], 'm'),
+                              (['s', 'seconds', 'sec', 'second',
+                                'S', 'Seconds', 'Sec', 'Second'], 's'),
+                              (['ms', 'milliseconds', 'millisecond', 'milli',
+                                'millis', 'l', 'MS', 'Milliseconds',
+                                'Millisecond', 'Milli', 'Millis', 'L'], 'ms'),
+                              (['us', 'microseconds', 'microsecond', 'micro',
+                                'micros', 'u', 'US', 'Microseconds',
+                                'Microsecond', 'Micro', 'Micros', 'U'], 'us'),
+                              (['ns', 'nanoseconds', 'nanosecond', 'nano',
+                                'nanos', 'n', 'NS', 'Nanoseconds',
+                                'Nanosecond', 'Nano', 'Nanos', 'N'], 'ns')])
+    @pytest.mark.parametrize('wrapper', [np.array, list, pd.Index])
+    def test_unit_parser(self, units, np_unit, wrapper):
+        # validate all units, GH 6855, GH 21762
+        for unit in units:
+            # array-likes
+            expected = TimedeltaIndex([np.timedelta64(i, np_unit)
                                        for i in np.arange(5).tolist()])
+            result = to_timedelta(wrapper(range(5)), unit=unit)
+            tm.assert_index_equal(result, expected)
+            result = TimedeltaIndex(wrapper(range(5)), unit=unit)
+            tm.assert_index_equal(result, expected)
+
+            if unit == 'M':
+                # M is treated as minutes in string repr
+                expected = TimedeltaIndex([np.timedelta64(i, 'm')
+                                           for i in np.arange(5).tolist()])
+
+            str_repr = ['{}{}'.format(x, unit) for x in np.arange(5)]
+            result = to_timedelta(wrapper(str_repr))
+            tm.assert_index_equal(result, expected)
+            result = TimedeltaIndex(wrapper(str_repr))
             tm.assert_index_equal(result, expected)
 
             # scalar
-            result = to_timedelta(2, unit=unit)
-            expected = Timedelta(np.timedelta64(2, transform(unit)).astype(
+            expected = Timedelta(np.timedelta64(2, np_unit).astype(
                 'timedelta64[ns]'))
-            assert result == expected
-
-        # validate all units
-        # GH 6855
-        for unit in ['Y', 'M', 'W', 'D', 'y', 'w', 'd']:
-            testit(unit, lambda x: x.upper())
-        for unit in ['days', 'day', 'Day', 'Days']:
-            testit(unit, lambda x: 'D')
-        for unit in ['h', 'm', 's', 'ms', 'us', 'ns', 'H', 'S', 'MS', 'US',
-                     'NS']:
-            testit(unit, lambda x: x.lower())
 
-        # offsets
+            result = to_timedelta(2, unit=unit)
+            assert result == expected
+            result = Timedelta(2, unit=unit)
+            assert result == expected
 
-        # m
-        testit('T', lambda x: 'm')
+            if unit == 'M':
+                expected = Timedelta(np.timedelta64(2, 'm').astype(
+                    'timedelta64[ns]'))
 
-        # ms
-        testit('L', lambda x: 'ms')
+            result = to_timedelta('2{}'.format(unit))
+            assert result == expected
+            result = Timedelta('2{}'.format(unit))
+            assert result == expected
 
     def test_numeric_conversions(self):
         assert ct(0) == np.timedelta64(0, 'ns')