Skip to content

Commit 183e8d0

Browse files
committed
Move towards parameterized tests; fix for np.datetime64 arith pandas-dev#7996
1 parent de663a1 commit 183e8d0

File tree

3 files changed

+141
-120
lines changed

3 files changed

+141
-120
lines changed

pandas/core/indexes/datetimelike.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ def __sub__(self, other):
681681
return self._add_delta(-other)
682682
elif is_integer(other):
683683
return self.shift(-other)
684-
elif isinstance(other, datetime):
684+
elif isinstance(other, (datetime, np.datetime64)):
685685
return self._sub_datelike(other)
686686
elif isinstance(other, Period):
687687
return self._sub_period(other)

pandas/core/indexes/datetimes.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -768,7 +768,7 @@ def _sub_datelike(self, other):
768768
raise TypeError("DatetimeIndex subtraction must have the same "
769769
"timezones or no timezones")
770770
result = self._sub_datelike_dti(other)
771-
elif isinstance(other, datetime):
771+
elif isinstance(other, (datetime, np.datetime64)):
772772
other = Timestamp(other)
773773
if other is libts.NaT:
774774
result = self._nat_new(box=False)
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,45 @@
11
""" Test Matrix for arithmetic operations on DatetimeIndex, TimedeltaIndex,
22
and PeriodIndex
33
"""
4+
from datetime import datetime, timedelta
45

56
import pytest
7+
import numpy as np
68

79
import pandas as pd
810
from pandas import Timestamp, Timedelta, NaT
911

12+
tdinat = pd.to_timedelta(['24658 days 11:15:00', 'NaT'])
13+
tdimax = pd.to_timedelta(['24658 days 11:15:00', Timedelta.max])
14+
tdimin = pd.to_timedelta(['24658 days 11:15:00', Timedelta.min])
15+
16+
dtinat = pd.to_datetime(['now', 'NaT'])
17+
dtimax = pd.to_datetime(['now', Timestamp.max])
18+
dtimin = pd.to_datetime(['now', Timestamp.min])
19+
20+
21+
tspos = Timestamp('1980-01-01')
22+
ts_pos_variants = [tspos,
23+
datetime(1980, 1, 1),
24+
np.datetime64('1980-01-01').astype('M8[ns]'),
25+
np.datetime64('1980-01-01').astype('M8[D]')]
26+
27+
tsneg = Timestamp('1950-01-01')
28+
ts_neg_variants = [tsneg,
29+
datetime(1950, 1, 1),
30+
np.datetime64('1950-01-01').astype('M8[ns]'),
31+
np.datetime64('1950-01-01').astype('M8[D]')]
32+
33+
tdpos = Timedelta('1h')
34+
td_pos_variants = [tdpos,
35+
tdpos.to_pytimedelta(),
36+
tdpos.to_timedelta64()]
37+
38+
tdneg = Timedelta('-1h')
39+
td_neg_variants = [tdneg,
40+
tdneg.to_pytimedelta(),
41+
tdneg.to_timedelta64()]
42+
1043

1144
class TestDatetimeLikeIndexArithmetic(object):
1245
# GH17991 checking for overflows and NaT masking on arithmetic ops
@@ -25,159 +58,147 @@ class TestDatetimeLikeIndexArithmetic(object):
2558
# - object-dtype, categorical dtype
2659
# - PeriodIndex
2760
# - consistency with .map(...) ?
61+
# - versions with near-min/max values
2862

2963
def test_timedeltaindex_add_timestamp_nat_masking(self):
30-
tdinat = pd.to_timedelta(['24658 days 11:15:00', 'NaT'])
31-
32-
# tsneg.value < 0, tspos.value > 0
33-
tsneg = Timestamp('1950-01-01')
34-
tspos = Timestamp('1980-01-01')
64+
for variant in ts_neg_variants:
65+
res = tdinat + variant
66+
assert res[1] is NaT
3567

36-
res1 = tdinat + tsneg
37-
assert res1[1] is NaT
38-
res2 = tdinat + tspos
39-
assert res2[1] is NaT
68+
for variant in ts_pos_variants:
69+
res = tdinat + variant
70+
assert res[1] is NaT
4071

4172
def test_timedeltaindex_add_timestamp_overflow(self):
42-
tdimax = pd.to_timedelta(['24658 days 11:15:00', Timedelta.max])
43-
tdimin = pd.to_timedelta(['24658 days 11:15:00', Timedelta.min])
44-
45-
# tsneg.value < 0, tspos.value > 0
46-
tsneg = Timestamp('1950-01-01')
47-
tspos = Timestamp('1980-01-01')
73+
expected = Timedelta.max.value + tsneg.value
74+
for variant in ts_neg_variants:
75+
res = tdimax + variant
76+
assert res[1].value == expected
4877

49-
res1 = tdimax + tsneg
50-
assert res1[1].value == Timedelta.max.value + tsneg.value
51-
res2 = tdimin + tspos
52-
assert res2[1].value == Timedelta.min.value + tspos.value
78+
expected = Timedelta.min.value + tspos.value
79+
for variant in ts_pos_variants:
80+
res = tdimin + variant
81+
assert res[1].value == expected
5382

54-
with pytest.raises(OverflowError):
55-
tdimax + tspos
83+
for variant in ts_pos_variants:
84+
with pytest.raises(OverflowError):
85+
tdimax + variant
5686

57-
with pytest.raises(OverflowError):
58-
tdimin + tsneg
87+
for variant in ts_neg_variants:
88+
with pytest.raises(OverflowError):
89+
tdimin + variant
5990

6091
def test_timedeltaindex_add_timedelta_overflow(self):
61-
tdimax = pd.to_timedelta(['24658 days 11:15:00', Timedelta.max])
62-
tdimin = pd.to_timedelta(['24658 days 11:15:00', Timedelta.min])
92+
for variant in td_pos_variants:
93+
with pytest.raises(OverflowError):
94+
tdimax + variant
6395

64-
# tdpos.value > 0, tdneg.value < 0
65-
tdpos = Timedelta('1h')
66-
tdneg = Timedelta('-1h')
96+
expected = Timedelta.max.value + tdneg.value
97+
for variant in td_neg_variants:
98+
res = tdimax + variant
99+
assert res[1].value == expected
67100

68-
with pytest.raises(OverflowError):
69-
tdimax + tdpos
101+
expected = Timedelta.min.value + tdpos.value
102+
for variant in td_pos_variants:
103+
res = tdimin + variant
104+
assert res[1].value == expected
70105

71-
res2 = tdimax + tdneg
72-
assert res2[1].value == Timedelta.max.value + tdneg.value
73-
res3 = tdimin + tdpos
74-
assert res3[1].value == Timedelta.min.value + tdpos.value
75-
76-
with pytest.raises(OverflowError):
77-
tdimin + tdneg
106+
for variant in td_neg_variants:
107+
with pytest.raises(OverflowError):
108+
tdimin + variant
78109

79110
def test_timedeltaindex_sub_timedelta_overflow(self):
80-
tdimax = pd.to_timedelta(['24658 days 11:15:00', Timedelta.max])
81-
tdimin = pd.to_timedelta(['24658 days 11:15:00', Timedelta.min])
82-
83-
# tdpos.value > 0, tdneg.value < 0
84-
tdpos = Timedelta('1h')
85-
tdneg = Timedelta('-1h')
86-
87-
res1 = tdimax - tdpos
88-
assert res1[1].value == Timedelta.max.value - tdpos.value
111+
expected = Timedelta.max.value - tdpos.value
112+
for variant in td_pos_variants:
113+
res1 = tdimax - variant
114+
assert res1[1].value == expected
89115

90-
with pytest.raises(OverflowError):
91-
tdimax - tdneg
116+
for variant in td_neg_variants:
117+
with pytest.raises(OverflowError):
118+
tdimax - variant
92119

93-
with pytest.raises(OverflowError):
94-
tdimin - tdpos
120+
for variant in td_pos_variants:
121+
with pytest.raises(OverflowError):
122+
tdimin - variant
95123

96-
res4 = tdimin - tdneg
97-
assert res4[1].value == Timedelta.min.value - tdneg.value
124+
expected = Timedelta.min.value - tdneg.value
125+
for variant in td_neg_variants:
126+
res = tdimin - variant
127+
assert res[1].value == expected
98128

99129
def test_datetimeindex_add_nat_masking(self):
100130
# Checking for NaTs and checking that we don't get an OverflowError
101-
dtinat = pd.to_datetime(['now', 'NaT'])
131+
for variant in td_pos_variants:
132+
res = dtinat + variant
133+
assert res[1] is NaT
102134

103-
# tdpos.value > 0, tdneg.value < 0
104-
tdpos = Timedelta('1h')
105-
tdneg = Timedelta('-1h')
106-
107-
res1 = dtinat + tdpos
108-
assert res1[1] is NaT
109-
res2 = dtinat + tdneg
110-
assert res2[1] is NaT
135+
for variant in td_neg_variants:
136+
res = dtinat + variant
137+
assert res[1] is NaT
111138

112139
def test_datetimeindex_sub_nat_masking(self):
113140
# Checking for NaTs and checking that we don't get an OverflowError
114-
dtinat = pd.to_datetime(['now', 'NaT'])
115-
116-
# tdpos.value > 0, tdneg.value < 0
117-
tdpos = Timedelta('1h')
118-
tdneg = Timedelta('-1h')
141+
for variant in td_pos_variants:
142+
res = dtinat - variant
143+
assert res[1] is NaT
119144

120-
res1 = dtinat - tdpos
121-
assert res1[1] is NaT
122-
res2 = dtinat - tdneg
123-
assert res2[1] is NaT
145+
for variant in td_neg_variants:
146+
res = dtinat - variant
147+
assert res[1] is NaT
124148

125149
def test_datetimeindex_add_timedelta_overflow(self):
126-
dtimax = pd.to_datetime(['now', Timestamp.max])
127-
dtimin = pd.to_datetime(['now', Timestamp.min])
128-
129-
# tdpos.value < 0, tdneg.value > 0
130-
tdpos = Timedelta('1h')
131-
tdneg = Timedelta('-1h')
150+
for variant in td_pos_variants:
151+
with pytest.raises(OverflowError):
152+
dtimax + variant
132153

133-
with pytest.raises(OverflowError):
134-
dtimax + tdpos
154+
expected = Timestamp.max.value + tdneg.value
155+
for variant in td_neg_variants:
156+
res = dtimax + variant
157+
assert res[1].value == expected
135158

136-
res2 = dtimax + tdneg
137-
assert res2[1].value == Timestamp.max.value + tdneg.value
159+
expected = Timestamp.min.value + tdpos.value
160+
for variant in td_pos_variants:
161+
res = dtimin + variant
162+
assert res[1].value == expected
138163

139-
res3 = dtimin + tdpos
140-
assert res3[1].value == Timestamp.min.value + tdpos.value
141-
142-
with pytest.raises(OverflowError):
143-
dtimin + tdneg
164+
for variant in td_neg_variants:
165+
with pytest.raises(OverflowError):
166+
dtimin + variant
144167

145168
def test_datetimeindex_sub_timedelta_overflow(self):
146-
dtimax = pd.to_datetime(['now', Timestamp.max])
147-
dtimin = pd.to_datetime(['now', Timestamp.min])
148-
149-
# tdpos.value < 0, tdneg.value > 0
150-
tdpos = Timedelta('1h')
151-
tdneg = Timedelta('-1h')
152-
153-
res1 = dtimax - tdpos
154-
assert res1[1].value == Timestamp.max.value - tdpos.value
169+
expected = Timestamp.max.value - tdpos.value
170+
for variant in td_pos_variants:
171+
res = dtimax - variant
172+
assert res[1].value == expected
155173

156-
with pytest.raises(OverflowError):
157-
dtimax - tdneg
174+
for variant in td_neg_variants:
175+
with pytest.raises(OverflowError):
176+
dtimax - variant
158177

159-
with pytest.raises(OverflowError):
160-
dtimin - tdpos
178+
for variant in td_pos_variants:
179+
with pytest.raises(OverflowError):
180+
dtimin - variant
161181

162-
res4 = dtimin - tdneg
163-
assert res4[1].value == Timestamp.min.value - tdneg.value
182+
expected = Timestamp.min.value - tdneg.value
183+
for variant in td_neg_variants:
184+
res = dtimin - variant
185+
assert res[1].value == expected
164186

165187
def test_datetimeindex_sub_timestamp_overflow(self):
166-
dtimax = pd.to_datetime(['now', Timestamp.max])
167-
dtimin = pd.to_datetime(['now', Timestamp.min])
168-
169-
# tsneg.value < 0, tspos.value > 0
170-
tsneg = Timestamp('1950-01-01')
171-
tspos = Timestamp('1980-01-01')
172-
173-
with pytest.raises(OverflowError):
174-
dtimax - tsneg
175-
176-
res2 = dtimax - tspos
177-
assert res2[1].value == Timestamp.max.value - tspos.value
178-
179-
res3 = dtimin - tsneg
180-
assert res3[1].value == Timestamp.min.value - tsneg.value
181-
182-
with pytest.raises(OverflowError):
183-
dtimin - tspos
188+
for variant in ts_neg_variants:
189+
with pytest.raises(OverflowError):
190+
dtimax - variant
191+
192+
expected = Timestamp.max.value - tspos.value
193+
for variant in ts_pos_variants:
194+
res = dtimax - variant
195+
assert res[1].value == expected
196+
197+
expected = Timestamp.min.value - tsneg.value
198+
for variant in ts_neg_variants:
199+
res = dtimin - variant
200+
assert res[1].value == expected
201+
202+
for variant in ts_pos_variants:
203+
with pytest.raises(OverflowError):
204+
dtimin - variant

0 commit comments

Comments
 (0)