Skip to content

Commit 1d3fa48

Browse files
committed
BUG #31793 subtract datetime from Timestamp
1 parent 0c18cc6 commit 1d3fa48

File tree

4 files changed

+38
-20
lines changed

4 files changed

+38
-20
lines changed

pandas/_libs/tslibs/timestamps.pyx

+4-4
Original file line numberDiff line numberDiff line change
@@ -305,10 +305,10 @@ cdef class _Timestamp(ABCTimestamp):
305305
else:
306306
self = type(other)(self)
307307

308-
# validate tz's
309-
if not tz_compare(self.tzinfo, other.tzinfo):
310-
raise TypeError("Timestamp subtraction must have the "
311-
"same timezones or no timezones")
308+
if (self.tzinfo is None) ^ (other.tzinfo is None):
309+
raise TypeError(
310+
"Cannot subtract offset-naive and offset-aware datetimes."
311+
)
312312

313313
# scalar Timestamp/datetime - Timestamp/datetime -> yields a
314314
# Timedelta

pandas/core/arrays/datetimes.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -712,11 +712,9 @@ def _sub_datetimelike_scalar(self, other):
712712
if other is NaT:
713713
return self - NaT
714714

715-
if not self._has_same_tz(other):
716-
# require tz compat
717-
raise TypeError(
718-
"Timestamp subtraction must have the same timezones or no timezones"
719-
)
715+
if (self.tzinfo is None) ^ (other.tzinfo is None):
716+
# require non-ambiguous timezones
717+
raise TypeError("Cannot subtract offset-naive and offset-aware datetimes.")
720718

721719
i8 = self.asi8
722720
result = checked_add_with_arr(i8, -other.value, arr_mask=self._isnan)

pandas/tests/arithmetic/test_timedelta64.py

+30-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Arithmetic tests for DataFrame/Series/Index/Array classes that should
22
# behave identically.
3-
from datetime import datetime, timedelta
3+
from datetime import datetime, timedelta, timezone
44

55
import numpy as np
66
import pytest
@@ -360,6 +360,8 @@ def test_subtraction_ops_with_tz(self):
360360
ts_tz2 = Timestamp("20130101").tz_localize("CET")
361361
dt_tz = ts_tz.to_pydatetime()
362362
td = Timedelta("1 days")
363+
ts_utc = Timestamp("2020-10-22T22:00:00+00:00")
364+
dt_utc = datetime(2020, 10, 22, 22, tzinfo=timezone.utc)
363365

364366
def _check(result, expected):
365367
assert result == expected
@@ -378,36 +380,54 @@ def _check(result, expected):
378380
expected = Timedelta("0 days")
379381
_check(result, expected)
380382

383+
result = ts_utc - dt_utc
384+
expected = Timedelta("0 days")
385+
_check(result, expected)
386+
387+
result = dt_tz - ts_tz2
388+
expected = Timedelta("0 days 06:00:00")
389+
_check(result, expected)
390+
391+
result = ts_tz2 - dt_tz
392+
expected = Timedelta("-1 days +18:00:00")
393+
_check(result, expected)
394+
395+
# gotta chedk this
381396
# tz mismatches
382-
msg = "Timestamp subtraction must have the same timezones or no timezones"
397+
msg = "Cannot subtract offset-naive and offset-aware datetimes."
383398
with pytest.raises(TypeError, match=msg):
384399
dt_tz - ts
385400
msg = "can't subtract offset-naive and offset-aware datetimes"
386401
with pytest.raises(TypeError, match=msg):
387402
dt_tz - dt
388-
msg = "Timestamp subtraction must have the same timezones or no timezones"
389-
with pytest.raises(TypeError, match=msg):
390-
dt_tz - ts_tz2
391403
msg = "can't subtract offset-naive and offset-aware datetimes"
392404
with pytest.raises(TypeError, match=msg):
393405
dt - dt_tz
394-
msg = "Timestamp subtraction must have the same timezones or no timezones"
406+
msg = "Cannot subtract offset-naive and offset-aware datetimes."
395407
with pytest.raises(TypeError, match=msg):
396408
ts - dt_tz
397409
with pytest.raises(TypeError, match=msg):
398410
ts_tz2 - ts
399411
with pytest.raises(TypeError, match=msg):
400412
ts_tz2 - dt
401-
with pytest.raises(TypeError, match=msg):
402-
ts_tz - ts_tz2
403413

404414
# with dti
405415
with pytest.raises(TypeError, match=msg):
406416
dti - ts_tz
407417
with pytest.raises(TypeError, match=msg):
408418
dti_tz - ts
409-
with pytest.raises(TypeError, match=msg):
410-
dti_tz - ts_tz2
419+
420+
result = dti_tz - ts_tz2
421+
expected = TimedeltaIndex(
422+
["0 days 06:00:00", "1 days 06:00:00", "2 days 06:00:00"]
423+
)
424+
tm.assert_index_equal(result, expected)
425+
426+
result = ts_tz2 - dti_tz
427+
expected = TimedeltaIndex(
428+
["-1 days +18:00:00", "-2 days +18:00:00", "-3 days +18:00:00"]
429+
)
430+
tm.assert_index_equal(result, expected)
411431

412432
result = dti_tz - dt_tz
413433
expected = TimedeltaIndex(["0 days", "1 days", "2 days"])

pandas/tests/scalar/timestamp/test_arithmetic.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def test_rsub_dtscalars(self, tz_naive_fixture):
9696
if tz_naive_fixture is None:
9797
assert other.to_datetime64() - ts == td
9898
else:
99-
msg = "subtraction must have"
99+
msg = "Cannot subtract offset-naive and offset-aware datetimes."
100100
with pytest.raises(TypeError, match=msg):
101101
other.to_datetime64() - ts
102102

0 commit comments

Comments
 (0)