Repository: pandas-dev/pandas

Issue:
BUG: index name lost with timedelta ops
```
import pandas as pd 

dtidx = pd.DatetimeIndex(['2011-01-01'], freq='D', name='dtidx')
(dtidx + 1).name
# dtidx

# NG
(dtidx + pd.Timedelta('1 day')).name
# None

tdidx = pd.TimedeltaIndex(['1 day'], freq='D', name='tdidx')
(tdidx + 1).name
# tdidx

# NG
(tdidx + pd.Timedelta('1 day')).name
# None
```

ref: #9862



Discussion:

Resolving patch:
diff --git a/pandas/core/common.py b/pandas/core/common.py
--- a/pandas/core/common.py
+++ b/pandas/core/common.py
@@ -3322,10 +3322,17 @@ def save(obj, path):  # TODO remove in 0.13
 
 
 def _maybe_match_name(a, b):
-    a_name = getattr(a, 'name', None)
-    b_name = getattr(b, 'name', None)
-    if a_name == b_name:
-        return a_name
+    a_has = hasattr(a, 'name')
+    b_has = hasattr(b, 'name')
+    if a_has and b_has:
+        if a.name == b.name:
+            return a.name
+        else:
+            return None
+    elif a_has:
+        return a.name
+    elif b_has:
+        return b.name
     return None
 
 def _random_state(state=None):
diff --git a/pandas/tseries/index.py b/pandas/tseries/index.py
--- a/pandas/tseries/index.py
+++ b/pandas/tseries/index.py
@@ -653,14 +653,18 @@ def _sub_datelike(self, other):
 
     def _add_delta(self, delta):
         from pandas import TimedeltaIndex
+        name = self.name
+
         if isinstance(delta, (Tick, timedelta, np.timedelta64)):
             new_values = self._add_delta_td(delta)
         elif isinstance(delta, TimedeltaIndex):
             new_values = self._add_delta_tdi(delta)
+            # update name when delta is Index
+            name = com._maybe_match_name(self, delta)
         else:
             new_values = self.astype('O') + delta
         tz = 'UTC' if self.tz is not None else None
-        result = DatetimeIndex(new_values, tz=tz, freq='infer')
+        result = DatetimeIndex(new_values, tz=tz, name=name, freq='infer')
         utc = _utc()
         if self.tz is not None and self.tz is not utc:
             result = result.tz_convert(self.tz)
diff --git a/pandas/tseries/tdi.py b/pandas/tseries/tdi.py
--- a/pandas/tseries/tdi.py
+++ b/pandas/tseries/tdi.py
@@ -281,12 +281,15 @@ def __setstate__(self, state):
     def _add_delta(self, delta):
         if isinstance(delta, (Tick, timedelta, np.timedelta64)):
             new_values = self._add_delta_td(delta)
+            name = self.name
         elif isinstance(delta, TimedeltaIndex):
             new_values = self._add_delta_tdi(delta)
+            # update name when delta is index
+            name = com._maybe_match_name(self, delta)
         else:
             raise ValueError("cannot add the type {0} to a TimedeltaIndex".format(type(delta)))
 
-        result = TimedeltaIndex(new_values, freq='infer')
+        result = TimedeltaIndex(new_values, freq='infer', name=name)
         return result
 
     def _evaluate_with_timedelta_like(self, other, op, opstr):


Test patch:
diff --git a/pandas/tests/test_common.py b/pandas/tests/test_common.py
--- a/pandas/tests/test_common.py
+++ b/pandas/tests/test_common.py
@@ -545,6 +545,27 @@ def test_random_state():
         com._random_state(5.5)
 
 
+def test_maybe_match_name():
+
+    matched = com._maybe_match_name(Series([1], name='x'), Series([2], name='x'))
+    assert(matched == 'x')
+
+    matched = com._maybe_match_name(Series([1], name='x'), Series([2], name='y'))
+    assert(matched is None)
+
+    matched = com._maybe_match_name(Series([1]), Series([2], name='x'))
+    assert(matched is None)
+
+    matched = com._maybe_match_name(Series([1], name='x'), Series([2]))
+    assert(matched is None)
+
+    matched = com._maybe_match_name(Series([1], name='x'), [2])
+    assert(matched == 'x')
+
+    matched = com._maybe_match_name([1], Series([2], name='y'))
+    assert(matched == 'y')
+
+
 class TestTake(tm.TestCase):
     # standard incompatible fill error
     fill_error = re.compile("Incompatible type for fill_value")
diff --git a/pandas/tseries/tests/test_base.py b/pandas/tseries/tests/test_base.py
--- a/pandas/tseries/tests/test_base.py
+++ b/pandas/tseries/tests/test_base.py
@@ -634,27 +634,27 @@ def test_dti_dti_deprecated_ops(self):
     def test_dti_tdi_numeric_ops(self):
 
         # These are normally union/diff set-like ops
-        tdi = TimedeltaIndex(['1 days',pd.NaT,'2 days'], name='foo')
-        dti = date_range('20130101',periods=3, name='bar')
+        tdi = TimedeltaIndex(['1 days', pd.NaT, '2 days'], name='foo')
+        dti = date_range('20130101', periods=3, name='bar')
         td = Timedelta('1 days')
         dt = Timestamp('20130101')
 
         result = tdi - tdi
         expected = TimedeltaIndex(['0 days', pd.NaT, '0 days'], name='foo')
-        tm.assert_index_equal(result, expected, check_names=False) # must be foo
+        tm.assert_index_equal(result, expected)
 
         result = tdi + tdi
         expected = TimedeltaIndex(['2 days', pd.NaT, '4 days'], name='foo')
-        tm.assert_index_equal(result, expected, check_names=False) # must be foo
+        tm.assert_index_equal(result, expected)
 
-        result = dti - tdi
+        result = dti - tdi   # name will be reset
         expected = DatetimeIndex(['20121231', pd.NaT, '20130101'])
         tm.assert_index_equal(result, expected)
 
     def test_addition_ops(self):
 
         # with datetimes/timedelta and tdi/dti
-        tdi = TimedeltaIndex(['1 days',pd.NaT,'2 days'], name='foo')
+        tdi = TimedeltaIndex(['1 days', pd.NaT, '2 days'], name='foo')
         dti = date_range('20130101', periods=3, name='bar')
         td = Timedelta('1 days')
         dt = Timestamp('20130101')
@@ -669,11 +669,11 @@ def test_addition_ops(self):
 
         result = td + tdi
         expected = TimedeltaIndex(['2 days', pd.NaT, '3 days'], name='foo')
-        tm.assert_index_equal(result, expected, check_names=False) # must be foo
+        tm.assert_index_equal(result, expected)
 
         result = tdi + td
         expected = TimedeltaIndex(['2 days', pd.NaT, '3 days'], name='foo')
-        tm.assert_index_equal(result,expected, check_names=False)  # must be foo
+        tm.assert_index_equal(result, expected)
 
         # unequal length
         self.assertRaises(ValueError, lambda : tdi + dti[0:1])
@@ -685,21 +685,21 @@ def test_addition_ops(self):
         # this is a union!
         #self.assertRaises(TypeError, lambda : Int64Index([1,2,3]) + tdi)
 
-        result = tdi + dti
+        result = tdi + dti   # name will be reset
         expected = DatetimeIndex(['20130102', pd.NaT, '20130105'])
-        tm.assert_index_equal(result,expected)
+        tm.assert_index_equal(result, expected)
 
-        result = dti + tdi
-        expected = DatetimeIndex(['20130102',pd.NaT,'20130105'])
-        tm.assert_index_equal(result,expected)
+        result = dti + tdi   # name will be reset
+        expected = DatetimeIndex(['20130102', pd.NaT, '20130105'])
+        tm.assert_index_equal(result, expected)
 
         result = dt + td
         expected = Timestamp('20130102')
-        self.assertEqual(result,expected)
+        self.assertEqual(result, expected)
 
         result = td + dt
         expected = Timestamp('20130102')
-        self.assertEqual(result,expected)
+        self.assertEqual(result, expected)
 
     def test_value_counts_unique(self):
         # GH 7735