Skip to content

Commit 23f6058

Browse files
committed
Merge pull request #3550 from jreback/ts_bug
BUG: raise on invalid operations for timedelta/datetime
2 parents be25266 + 50ddc3f commit 23f6058

File tree

3 files changed

+50
-13
lines changed

3 files changed

+50
-13
lines changed

RELEASE.rst

+2
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ pandas 0.11.1
9494
- Fixed bug in selecting month/quarter/year from a series would not select the time element
9595
on the last day (GH3546_)
9696
- Properly convert np.datetime64 objects in a Series (GH3416_)
97+
- Raise a TypeError on invalid datetime/timedelta operations
98+
e.g. add datetimes, multiple timedelta x datetime
9799

98100
.. _GH3164: https://github.com/pydata/pandas/issues/3164
99101
.. _GH2786: https://github.com/pydata/pandas/issues/2786

pandas/core/series.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def na_op(x, y):
7575

7676
return result
7777

78-
def wrapper(self, other):
78+
def wrapper(self, other, name=name):
7979
from pandas.core.frame import DataFrame
8080
dtype = None
8181
wrap_results = lambda x: x
@@ -123,6 +123,13 @@ def convert_to_array(values):
123123
# 2 datetimes or 2 timedeltas
124124
if (is_timedelta_lhs and is_timedelta_rhs) or (is_datetime_lhs and is_datetime_rhs):
125125

126+
if is_datetime_lhs and name not in ['__sub__']:
127+
raise TypeError("can only operate on a datetimes for subtraction, "
128+
"but the operator [%s] was passed" % name)
129+
elif is_timedelta_lhs and name not in ['__add__','__sub__']:
130+
raise TypeError("can only operate on a timedeltas for "
131+
"addition and subtraction, but the operator [%s] was passed" % name)
132+
126133
dtype = 'timedelta64[ns]'
127134

128135
# we may have to convert to object unfortunately here
@@ -135,6 +142,10 @@ def wrap_results(x):
135142

136143
# datetime and timedelta
137144
elif (is_timedelta_lhs and is_datetime_rhs) or (is_timedelta_rhs and is_datetime_lhs):
145+
146+
if name not in ['__add__','__sub__']:
147+
raise TypeError("can only operate on a timedelta and a datetime for "
148+
"addition and subtraction, but the operator [%s] was passed" % name)
138149
dtype = 'M8[ns]'
139150

140151
else:

pandas/tests/test_series.py

+36-12
Original file line numberDiff line numberDiff line change
@@ -1889,26 +1889,14 @@ def test_operators_timedelta64(self):
18891889
assert_series_equal(result,expected)
18901890
self.assert_(result.dtype=='m8[ns]')
18911891

1892-
result = df['A'] + datetime(2001,1,1)
1893-
expected = Series([timedelta(days=26663+i) for i in range(3)])
1894-
assert_series_equal(result,expected)
1895-
self.assert_(result.dtype=='m8[ns]')
1896-
18971892
d = datetime(2001,1,1,3,4)
18981893
resulta = df['A'] - d
18991894
self.assert_(resulta.dtype=='m8[ns]')
19001895

1901-
resultb = df['A'] + d
1902-
self.assert_(resultb.dtype=='m8[ns]')
1903-
19041896
# roundtrip
19051897
resultb = resulta + d
19061898
assert_series_equal(df['A'],resultb)
19071899

1908-
# timedelta on lhs
1909-
result = resultb + d
1910-
self.assert_(result.dtype=='m8[ns]')
1911-
19121900
# timedeltas on rhs
19131901
td = timedelta(days=1)
19141902
resulta = df['A'] + td
@@ -1931,6 +1919,42 @@ def test_operators_timedelta64(self):
19311919
self.assert_(result.dtype=='m8[ns]')
19321920
assert_series_equal(result,expected)
19331921

1922+
def test_operators_datetimelike(self):
1923+
1924+
### timedelta64 ###
1925+
td1 = Series([timedelta(minutes=5,seconds=3)]*3)
1926+
td2 = timedelta(minutes=5,seconds=4)
1927+
for op in ['__mul__','__floordiv__','__truediv__','__div__','__pow__']:
1928+
op = getattr(td1,op,None)
1929+
if op is not None:
1930+
self.assertRaises(TypeError, op, td2)
1931+
td1 + td2
1932+
td1 - td2
1933+
1934+
### datetime64 ###
1935+
dt1 = Series([Timestamp('20111230'),Timestamp('20120101'),Timestamp('20120103')])
1936+
dt2 = Series([Timestamp('20111231'),Timestamp('20120102'),Timestamp('20120104')])
1937+
for op in ['__add__','__mul__','__floordiv__','__truediv__','__div__','__pow__']:
1938+
op = getattr(dt1,op,None)
1939+
if op is not None:
1940+
self.assertRaises(TypeError, op, dt2)
1941+
dt1 - dt2
1942+
1943+
### datetime64 with timetimedelta ###
1944+
for op in ['__mul__','__floordiv__','__truediv__','__div__','__pow__']:
1945+
op = getattr(dt1,op,None)
1946+
if op is not None:
1947+
self.assertRaises(TypeError, op, td1)
1948+
dt1 + td1
1949+
dt1 - td1
1950+
1951+
### timetimedelta with datetime64 ###
1952+
for op in ['__mul__','__floordiv__','__truediv__','__div__','__pow__']:
1953+
op = getattr(td1,op,None)
1954+
if op is not None:
1955+
self.assertRaises(TypeError, op, dt1)
1956+
td1 + dt1
1957+
td1 - dt1
19341958

19351959
def test_timedelta64_functions(self):
19361960

0 commit comments

Comments
 (0)