Skip to content

BUG: Fix timedelta64+Timestamp, closes #24775 #26916

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 25, 2019
2 changes: 2 additions & 0 deletions doc/source/whatsnew/v0.25.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,8 @@ Datetimelike
- Bug when comparing a :class:`PeriodIndex` against a zero-dimensional numpy array (:issue:`26689`)
- Bug in constructing a ``Series`` or ``DataFrame`` from a numpy ``datetime64`` array with a non-ns unit and out-of-bound timestamps generating rubbish data, which will now correctly raise an ``OutOfBoundsDatetime`` error (:issue:`26206`).
- Bug in :func:`date_range` with unnecessary ``OverflowError`` being raised for very large or very small dates (:issue:`26651`)
- Bug where adding :class:`Timestamp` to a ``np.timedelta64`` object would raise instead of returning a :class:`Timestamp` (:issue:`24775`)
- Bug where comparing a zero-dimensional numpy array containing a ``np.datetime64`` object to a :class:`Timestamp` would incorrect raise ``TypeError`` (:issue:`26916`)

Timedelta
^^^^^^^^^
Expand Down
12 changes: 12 additions & 0 deletions pandas/_libs/tslibs/c_timestamp.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ def maybe_integer_op_deprecated(obj):

cdef class _Timestamp(datetime):

# higher than np.ndarray and np.matrix
__array_priority__ = 100

def __hash__(_Timestamp self):
if self.nanosecond:
return hash(self.value)
Expand Down Expand Up @@ -85,6 +88,15 @@ cdef class _Timestamp(datetime):
if ndim == 0:
if is_datetime64_object(other):
other = self.__class__(other)
elif is_array(other):
# zero-dim array, occurs if try comparison with
# datetime64 scalar on the left hand side
# Unfortunately, for datetime64 values, other.item()
# incorrectly returns an integer, so we need to use
# the numpy C api to extract it.
other = cnp.PyArray_ToScalar(cnp.PyArray_DATA(other),
other)
other = self.__class__(other)
else:
return NotImplemented
elif is_array(other):
Expand Down
20 changes: 20 additions & 0 deletions pandas/tests/scalar/timestamp/test_arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,23 @@ def test_addition_subtraction_preserve_frequency(self):
td64 = np.timedelta64(1, 'D')
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):
# GH#24775 timedelta64+Timestamp should not raise
ts = Timestamp.now()
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
12 changes: 12 additions & 0 deletions pandas/tests/scalar/timestamp/test_comparisons.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,18 @@ def test_timestamp_compare_with_early_datetime(self):
assert stamp < datetime(2700, 1, 1)
assert stamp <= datetime(2700, 1, 1)

def test_compare_zerodim_array(self):
# GH#26916
ts = Timestamp.now()
dt64 = np.datetime64('2016-01-01', 'ns')
arr = np.array(dt64)
assert arr.ndim == 0

result = arr < ts
assert result is True
result = arr > ts
assert result is False


def test_rich_comparison_with_unsupported_type():
# Comparisons with unsupported objects should return NotImplemented
Expand Down
25 changes: 0 additions & 25 deletions pandas/tests/scalar/timestamp/test_timestamp.py
Original file line number Diff line number Diff line change
Expand Up @@ -796,31 +796,6 @@ def test_tz_conversion_freq(self, tz_naive_fixture):

class TestTimestampNsOperations:

def setup_method(self, method):
self.timestamp = Timestamp(datetime.utcnow())

def assert_ns_timedelta(self, modified_timestamp, expected_value):
value = self.timestamp.value
modified_value = modified_timestamp.value

assert modified_value - value == expected_value

def test_timedelta_ns_arithmetic(self):
self.assert_ns_timedelta(self.timestamp + np.timedelta64(-123, 'ns'),
-123)

def test_timedelta_ns_based_arithmetic(self):
self.assert_ns_timedelta(self.timestamp + np.timedelta64(
1234567898, 'ns'), 1234567898)

def test_timedelta_us_arithmetic(self):
self.assert_ns_timedelta(self.timestamp + np.timedelta64(-123, 'us'),
-123000)

def test_timedelta_ms_arithmetic(self):
time = self.timestamp + np.timedelta64(-123, 'ms')
self.assert_ns_timedelta(time, -123000000)

def test_nanosecond_string_parsing(self):
ts = Timestamp('2013-05-01 07:15:45.123456789')
# GH 7878
Expand Down