forked from pandas-dev/pandas
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathtest_arithmetic.py
321 lines (255 loc) · 10.9 KB
/
test_arithmetic.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
from datetime import (
datetime,
timedelta,
timezone,
)
import numpy as np
import pytest
from pandas._libs.tslibs import (
OutOfBoundsDatetime,
OutOfBoundsTimedelta,
Timedelta,
Timestamp,
offsets,
to_offset,
)
import pandas._testing as tm
class TestTimestampArithmetic:
def test_overflow_offset(self):
# no overflow expected
stamp = Timestamp("2000/1/1")
offset_no_overflow = to_offset("D") * 100
expected = Timestamp("2000/04/10")
assert stamp + offset_no_overflow == expected
assert offset_no_overflow + stamp == expected
expected = Timestamp("1999/09/23")
assert stamp - offset_no_overflow == expected
def test_overflow_offset_raises(self):
# xref https://github.com/statsmodels/statsmodels/issues/3374
# ends up multiplying really large numbers which overflow
stamp = Timestamp("2017-01-13 00:00:00")
offset_overflow = 20169940 * offsets.Day(1)
msg = (
"the add operation between "
r"\<-?\d+ \* Days\> and \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} "
"will overflow"
)
lmsg2 = r"Cannot cast <-?20169940 \* Days> to unit=ns without overflow"
with pytest.raises(OutOfBoundsTimedelta, match=lmsg2):
stamp + offset_overflow
with pytest.raises(OverflowError, match=msg):
offset_overflow + stamp
with pytest.raises(OutOfBoundsTimedelta, match=lmsg2):
stamp - offset_overflow
# xref https://github.com/pandas-dev/pandas/issues/14080
# used to crash, so check for proper overflow exception
stamp = Timestamp("2000/1/1")
offset_overflow = to_offset("D") * 100**5
lmsg3 = r"Cannot cast <-?10000000000 \* Days> to unit=ns without overflow"
with pytest.raises(OutOfBoundsTimedelta, match=lmsg3):
stamp + offset_overflow
with pytest.raises(OverflowError, match=msg):
offset_overflow + stamp
with pytest.raises(OutOfBoundsTimedelta, match=lmsg3):
stamp - offset_overflow
def test_overflow_timestamp_raises(self):
# https://github.com/pandas-dev/pandas/issues/31774
msg = "Result is too large"
a = Timestamp("2101-01-01 00:00:00")
b = Timestamp("1688-01-01 00:00:00")
with pytest.raises(OutOfBoundsDatetime, match=msg):
a - b
# but we're OK for timestamp and datetime.datetime
assert (a - b.to_pydatetime()) == (a.to_pydatetime() - b)
def test_delta_preserve_nanos(self):
val = Timestamp(1337299200000000123)
result = val + timedelta(1)
assert result.nanosecond == val.nanosecond
def test_rsub_dtscalars(self, tz_naive_fixture):
# In particular, check that datetime64 - Timestamp works GH#28286
td = Timedelta(1235345642000)
ts = Timestamp("2021-01-01", tz=tz_naive_fixture)
other = ts + td
assert other - ts == td
assert other.to_pydatetime() - ts == td
if tz_naive_fixture is None:
assert other.to_datetime64() - ts == td
else:
msg = "Cannot subtract tz-naive and tz-aware datetime-like objects"
with pytest.raises(TypeError, match=msg):
other.to_datetime64() - ts
def test_timestamp_sub_datetime(self):
dt = datetime(2013, 10, 12)
ts = Timestamp(datetime(2013, 10, 13))
assert (ts - dt).days == 1
assert (dt - ts).days == -1
def test_subtract_tzaware_datetime(self):
t1 = Timestamp("2020-10-22T22:00:00+00:00")
t2 = datetime(2020, 10, 22, 22, tzinfo=timezone.utc)
result = t1 - t2
assert isinstance(result, Timedelta)
assert result == Timedelta("0 days")
def test_subtract_timestamp_from_different_timezone(self):
t1 = Timestamp("20130101").tz_localize("US/Eastern")
t2 = Timestamp("20130101").tz_localize("CET")
result = t1 - t2
assert isinstance(result, Timedelta)
assert result == Timedelta("0 days 06:00:00")
def test_subtracting_involving_datetime_with_different_tz(self):
t1 = datetime(2013, 1, 1, tzinfo=timezone(timedelta(hours=-5)))
t2 = Timestamp("20130101").tz_localize("CET")
result = t1 - t2
assert isinstance(result, Timedelta)
assert result == Timedelta("0 days 06:00:00")
result = t2 - t1
assert isinstance(result, Timedelta)
assert result == Timedelta("-1 days +18:00:00")
def test_subtracting_different_timezones(self, tz_aware_fixture):
t_raw = Timestamp("20130101")
t_UTC = t_raw.tz_localize("UTC")
t_diff = t_UTC.tz_convert(tz_aware_fixture) + Timedelta("0 days 05:00:00")
result = t_diff - t_UTC
assert isinstance(result, Timedelta)
assert result == Timedelta("0 days 05:00:00")
def test_addition_subtraction_types(self):
# Assert on the types resulting from Timestamp +/- various date/time
# objects
dt = datetime(2014, 3, 4)
td = timedelta(seconds=1)
# build a timestamp with a frequency, since then it supports
# addition/subtraction of integers
with tm.assert_produces_warning(FutureWarning, match="The 'freq' argument"):
# freq deprecated
ts = Timestamp(dt, freq="D")
msg = "Addition/subtraction of integers"
with pytest.raises(TypeError, match=msg):
# GH#22535 add/sub with integers is deprecated
ts + 1
with pytest.raises(TypeError, match=msg):
ts - 1
# Timestamp + datetime not supported, though subtraction is supported
# and yields timedelta more tests in tseries/base/tests/test_base.py
assert type(ts - dt) == Timedelta
assert type(ts + td) == Timestamp
assert type(ts - td) == Timestamp
# Timestamp +/- datetime64 not supported, so not tested (could possibly
# assert error raised?)
td64 = np.timedelta64(1, "D")
assert type(ts + td64) == Timestamp
assert type(ts - td64) == Timestamp
@pytest.mark.parametrize(
"freq, td, td64",
[
("S", timedelta(seconds=1), np.timedelta64(1, "s")),
("min", timedelta(minutes=1), np.timedelta64(1, "m")),
("H", timedelta(hours=1), np.timedelta64(1, "h")),
("D", timedelta(days=1), np.timedelta64(1, "D")),
("W", timedelta(weeks=1), np.timedelta64(1, "W")),
("M", None, np.timedelta64(1, "M")),
],
)
@pytest.mark.filterwarnings("ignore:Timestamp.freq is deprecated:FutureWarning")
@pytest.mark.filterwarnings("ignore:The 'freq' argument:FutureWarning")
def test_addition_subtraction_preserve_frequency(self, freq, td, td64):
ts = Timestamp("2014-03-05 00:00:00", freq=freq)
original_freq = ts.freq
assert (ts + 1 * original_freq).freq == original_freq
assert (ts - 1 * original_freq).freq == original_freq
if td is not None:
# timedelta does not support months as unit
assert (ts + td).freq == original_freq
assert (ts - td).freq == original_freq
assert (ts + td64).freq == original_freq
assert (ts - td64).freq == original_freq
@pytest.mark.parametrize(
"td", [Timedelta(hours=3), np.timedelta64(3, "h"), timedelta(hours=3)]
)
def test_radd_tdscalar(self, td, fixed_now_ts):
# GH#24775 timedelta64+Timestamp should not raise
ts = fixed_now_ts
assert td + ts == ts + td
@pytest.mark.parametrize(
"other,expected_difference",
[
(np.timedelta64(-123, "ns"), -123),
(np.timedelta64(1234567898, "ns"), 1234567898),
(np.timedelta64(-123, "us"), -123000),
(np.timedelta64(-123, "ms"), -123000000),
],
)
def test_timestamp_add_timedelta64_unit(self, other, expected_difference):
ts = Timestamp(datetime.utcnow())
result = ts + other
valdiff = result.value - ts.value
assert valdiff == expected_difference
@pytest.mark.parametrize(
"ts",
[
Timestamp("1776-07-04"),
Timestamp("1776-07-04", tz="UTC"),
],
)
@pytest.mark.parametrize(
"other",
[
1,
np.int64(1),
np.array([1, 2], dtype=np.int32),
np.array([3, 4], dtype=np.uint64),
],
)
def test_add_int_with_freq(self, ts, other):
msg = "Addition/subtraction of integers and integer-arrays"
with pytest.raises(TypeError, match=msg):
ts + other
with pytest.raises(TypeError, match=msg):
other + ts
with pytest.raises(TypeError, match=msg):
ts - other
msg = "unsupported operand type"
with pytest.raises(TypeError, match=msg):
other - ts
@pytest.mark.parametrize("shape", [(6,), (2, 3)])
def test_addsub_m8ndarray(self, shape):
# GH#33296
ts = Timestamp("2020-04-04 15:45")
other = np.arange(6).astype("m8[h]").reshape(shape)
result = ts + other
ex_stamps = [ts + Timedelta(hours=n) for n in range(6)]
expected = np.array([x.asm8 for x in ex_stamps], dtype="M8[ns]").reshape(shape)
tm.assert_numpy_array_equal(result, expected)
result = other + ts
tm.assert_numpy_array_equal(result, expected)
result = ts - other
ex_stamps = [ts - Timedelta(hours=n) for n in range(6)]
expected = np.array([x.asm8 for x in ex_stamps], dtype="M8[ns]").reshape(shape)
tm.assert_numpy_array_equal(result, expected)
msg = r"unsupported operand type\(s\) for -: 'numpy.ndarray' and 'Timestamp'"
with pytest.raises(TypeError, match=msg):
other - ts
@pytest.mark.parametrize("shape", [(6,), (2, 3)])
def test_addsub_m8ndarray_tzaware(self, shape):
# GH#33296
ts = Timestamp("2020-04-04 15:45", tz="US/Pacific")
other = np.arange(6).astype("m8[h]").reshape(shape)
result = ts + other
ex_stamps = [ts + Timedelta(hours=n) for n in range(6)]
expected = np.array(ex_stamps).reshape(shape)
tm.assert_numpy_array_equal(result, expected)
result = other + ts
tm.assert_numpy_array_equal(result, expected)
result = ts - other
ex_stamps = [ts - Timedelta(hours=n) for n in range(6)]
expected = np.array(ex_stamps).reshape(shape)
tm.assert_numpy_array_equal(result, expected)
msg = r"unsupported operand type\(s\) for -: 'numpy.ndarray' and 'Timestamp'"
with pytest.raises(TypeError, match=msg):
other - ts
def test_subtract_different_utc_objects(self, utc_fixture, utc_fixture2):
# GH 32619
dt = datetime(2021, 1, 1)
ts1 = Timestamp(dt, tz=utc_fixture)
ts2 = Timestamp(dt, tz=utc_fixture2)
result = ts1 - ts2
expected = Timedelta(0)
assert result == expected