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
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").as_unit("ns")
        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 \+?00:00:00 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").as_unit("ns")
        offset_overflow = to_offset("D") * 100**5
 
        lmsg3 = (
            r"Cannot cast -?10000000000 days \+?00:00:00 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").as_unit("ns")
        b = Timestamp("1688-01-01 00:00:00").as_unit("ns")
 
        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)
        ts = Timestamp(dt)
 
        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(
        "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):
        now = datetime.utcnow()
        ts = Timestamp(now).as_unit("ns")
        result = ts + other
        valdiff = result._value - ts._value
        assert valdiff == expected_difference
 
        ts2 = Timestamp(now)
        assert ts2 + other == result
 
    @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").as_unit("ns")
        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