Repository: pandas-dev/pandas

Issue:
Possible Bug - math on like-indexed datetime series doesn't work as expected
![firstordernoteval](https://cloud.githubusercontent.com/assets/4734283/3319074/b707a340-f71c-11e3-942f-c06129ab246f.jpg)
![firstevalorder](https://cloud.githubusercontent.com/assets/4734283/3319075/bb0908d0-f71c-11e3-828f-2dd601125732.jpg)

I have two series that are like-indexed datetimes. I'm trying to do simple math operations on them and noticed the results don't match what I'd expect. Specifically, subtracting one datetime from the other doesn't always result in subtraction across the aligned indices. Transforming the series to a dataframe with a dummy column gets us closer but the type manipulation isn't correct. 

``` python
print firstOrderNotEval.loc[site]
print firstEvalOrder.loc[site]
print type(firstOrderNotEval.loc[site])
print type(firstEvalOrder.loc[site])

### output:
#2008-08-21 00:00:00
#2013-09-10 00:00:00
# <class 'pandas.tslib.Timestamp'>
# <class 'pandas.tslib.Timestamp'>

timeToFirstNonEvalPurchase_doesntWork = ((firstOrderNotEval - firstEvalOrder)/np.timedelta64(1,'D'))
timeToFirstNonEvalPurchase = ((firstOrderNotEval.to_frame('a') - firstEvalOrder.to_frame('a'))/np.timedelta64(1,'D'))['a']

print timeToFirstNonEvalPurchase_doesntWork.loc[2898717]
print timeToFirstNonEvalPurchase.loc[2898717]

### output:
# nan
# -1846 nanoseconds # note should be 1846 days

```

Subtracting individual elements gives the correct result but as a datetime.timedelta type. subtracting the series directly gives NaT: 

``` python
site = 2898717   
print (firstOrderNotEval.loc[site] - firstEvalOrder.loc[site])
print type(firstOrderNotEval.loc[site] - firstEvalOrder.loc[site])
print (firstOrderNotEval - firstEvalOrder).loc[site]
### output:
# -1846 days, 0:00:00
# <type 'datetime.timedelta'>
# NaT
```

Perhaps this has to do with the timestamp type itself given the following example:

``` python
print (firstOrderNotEval.to_frame('a') - firstEvalOrder.to_frame('a')).loc[site]/np.timedelta64(1,'D')
print ((firstOrderNotEval.to_frame('a') - firstEvalOrder.to_frame('a'))/np.timedelta64(1,'D')).loc[site]

### output:
# a   -1846
# Name: 2898717.0, dtype: float64
# a   -00:00:00.000002
# Name: 2898717.0, dtype: timedelta64[ns]
```

Note that the following have different results based on how the divide by timedelta64 is performed:

``` python
tmp = ((firstOrderNotEval.to_frame('a') - firstEvalOrder.to_frame('a')))
print (tmp/np.timedelta64(1,'D')).loc[site]
print tmp.apply(lambda x: x/np.timedelta64(1,'D')).loc[site]
### output:
# a   -00:00:00.000002
# Name: 2898717.0, dtype: timedelta64[ns]
### what we'd expect: 
# a   -1846
# Name: 2898717.0, dtype: float64
```

The attached pickle files (as .jpg) include the series used in this example

``` python
firstOrderNotEval.to_pickle('./firstOrderNotEval.jpg')
firstEvalOrder.to_pickle('./firstEvalOrder.jpg')
```



Discussion:
show what `pd.show_versions()` displays

so you need to align the series first (this is a bug, should be done internally).

`x,y = firstOrderNotEval.align(firstEvalOrder)`

then

`x-y` will be fine


Resolving patch:
diff --git a/pandas/core/ops.py b/pandas/core/ops.py
--- a/pandas/core/ops.py
+++ b/pandas/core/ops.py
@@ -245,6 +245,12 @@ class _TimeOp(object):
     def __init__(self, left, right, name):
         self.name = name
 
+        # need to make sure that we are aligning the data
+        if isinstance(left, pd.Series) and isinstance(right, pd.Series):
+            left, right = left.align(right)
+
+        self.left = left
+        self.right = right
         lvalues = self._convert_to_array(left, name=name)
         rvalues = self._convert_to_array(right, name=name, other=lvalues)
 
@@ -426,6 +432,7 @@ def maybe_convert_for_time_op(cls, left, right, name):
         is_datetime_lhs = com.is_datetime64_dtype(left)
         if not (is_datetime_lhs or is_timedelta_lhs):
             return None
+
         # rops are allowed. No need for special checks, just strip off
         # r part.
         if name.startswith('__r'):
@@ -463,6 +470,7 @@ def wrapper(left, right, name=name):
 
         if isinstance(right, pd.DataFrame):
             return NotImplemented
+
         time_converted = _TimeOp.maybe_convert_for_time_op(left, right, name)
 
         if time_converted is None:
@@ -472,8 +480,8 @@ def wrapper(left, right, name=name):
         elif time_converted == NotImplemented:
             return NotImplemented
         else:
-            lvalues = time_converted.lvalues
-            rvalues = time_converted.rvalues
+            left, right = time_converted.left, time_converted.right
+            lvalues, rvalues = time_converted.lvalues, time_converted.rvalues
             dtype = time_converted.dtype
             wrap_results = time_converted.wrap_results
 


Test patch:
diff --git a/pandas/tests/test_series.py b/pandas/tests/test_series.py
--- a/pandas/tests/test_series.py
+++ b/pandas/tests/test_series.py
@@ -2838,6 +2838,24 @@ def run_ops(ops, get_ser, test_ser):
         td1 + dt1
         dt1 + td1
 
+    def test_ops_datetimelike_align(self):
+        if _np_version_under1p7:
+            raise nose.SkipTest("timedelta broken in np < 1.7")
+
+        # GH 7500
+        # datetimelike ops need to align
+        dt = Series(date_range('2012-1-1', periods=3, freq='D'))
+        dt.iloc[2] = np.nan
+        dt2 = dt[::-1]
+
+        expected = Series([timedelta(0),timedelta(0),pd.NaT])
+
+        result = dt2-dt
+        assert_series_equal(result,expected)
+
+        result = (dt2.to_frame()-dt.to_frame())[0]
+        assert_series_equal(result,expected)
+
     def test_timedelta64_functions(self):
 
         from datetime import timedelta