Skip to content

FIX BUG: Timestamp __add__/__sub__ DateOffset with nanoseconds lost. #43968

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 38 commits into from
Dec 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
2208507
fix Timestamp + Dateoffset bugs.
tushushu Oct 11, 2021
ee19a4d
Add doc string and fix UT.
tushushu Oct 16, 2021
3e38ab2
fix UT
tushushu Oct 16, 2021
5718e4f
Trim Trailing Whitespace
tushushu Oct 16, 2021
e1ae149
remove is_dateoffset_obj func
tushushu Oct 17, 2021
326790b
explicitly check the Offset type
tushushu Oct 17, 2021
83453da
fix bugs.
tushushu Oct 17, 2021
ae4b133
fix bugs
tushushu Oct 17, 2021
a43030d
try to fix __sub__ method.
tushushu Oct 17, 2021
22cadbf
fix flake8 check.
tushushu Oct 17, 2021
1c89c03
add tests for dateoffset add method.
tushushu Oct 17, 2021
a2ad274
fix offset init.
tushushu Oct 17, 2021
9bc6e97
fix black code style check.
tushushu Oct 18, 2021
c9ddf14
test sub method.
tushushu Oct 18, 2021
4780316
add an comment.
tushushu Oct 19, 2021
1fecfa6
move the nano handling codes to Offsets.
tushushu Oct 23, 2021
e92c1a9
remove unnecessary codes.
tushushu Oct 23, 2021
006c073
fix bugs
tushushu Oct 24, 2021
ab064b4
more test cases for dateoffset add/sub
tushushu Oct 24, 2021
8e42c64
test radd.
tushushu Oct 24, 2021
5ca3472
code style.
tushushu Oct 25, 2021
37d19b5
remove unused line.
tushushu Oct 30, 2021
5acad37
Merge branch 'pandas-dev:master' into fix-bug-dateoffset-nanoseconds
tushushu Dec 2, 2021
9cda54a
what's new
tushushu Dec 2, 2021
0ee9a6f
remove unnecessary brackets
tushushu Dec 2, 2021
860a62b
what's new.
tushushu Dec 3, 2021
4600a64
makes the comment more detailed.
tushushu Dec 3, 2021
8cf5746
test case as jbrockmendel demand
tushushu Dec 4, 2021
a210baf
trigger the CI without a commit
tushushu Dec 5, 2021
98646cd
Merge branch 'pandas-dev:master' into fix-bug-dateoffset-nanoseconds
tushushu Dec 5, 2021
e899fbe
Merge branch 'pandas-dev:master' into fix-bug-dateoffset-nanoseconds
tushushu Dec 6, 2021
906f546
Merge branch 'pandas-dev:master' into fix-bug-dateoffset-nanoseconds
tushushu Dec 7, 2021
24fe768
split dateoffset test func
tushushu Dec 7, 2021
209ba9e
Merge branch 'fix-bug-dateoffset-nanoseconds' of github.com:tushushu/…
tushushu Dec 7, 2021
f56f52b
Merge branch 'pandas-dev:master' into fix-bug-dateoffset-nanoseconds
tushushu Dec 7, 2021
3f49876
fix typo
tushushu Dec 8, 2021
b7297ef
Merge branch 'fix-bug-dateoffset-nanoseconds' of github.com:tushushu/…
tushushu Dec 8, 2021
5cbae96
Merge branch 'pandas-dev:master' into fix-bug-dateoffset-nanoseconds
tushushu Dec 17, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/whatsnew/v1.4.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,7 @@ Datetimelike
- Bug in adding a ``np.timedelta64`` object to a :class:`BusinessDay` or :class:`CustomBusinessDay` object incorrectly raising (:issue:`44532`)
- Bug in :meth:`Index.insert` for inserting ``np.datetime64``, ``np.timedelta64`` or ``tuple`` into :class:`Index` with ``dtype='object'`` with negative loc adding ``None`` and replacing existing value (:issue:`44509`)
- Bug in :meth:`Series.mode` with ``DatetimeTZDtype`` incorrectly returning timezone-naive and ``PeriodDtype`` incorrectly raising (:issue:`41927`)
- Bug in :class:`DateOffset`` addition with :class:`Timestamp` where ``offset.nanoseconds`` would not be included in the result. (:issue:`43968`)
-

Timedelta
Expand Down
16 changes: 11 additions & 5 deletions pandas/_libs/tslibs/offsets.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,9 @@ def apply_wraps(func):
if self.normalize:
result = result.normalize()

# nanosecond may be deleted depending on offset process
if not self.normalize and nano != 0:
# If the offset object does not have a nanoseconds component,
# the result's nanosecond component may be lost.
if not self.normalize and nano != 0 and not hasattr(self, "nanoseconds"):
if result.nanosecond != nano:
if result.tz is not None:
# convert to UTC
Expand Down Expand Up @@ -333,7 +334,7 @@ cdef _determine_offset(kwds):
# sub-daily offset - use timedelta (tz-aware)
offset = timedelta(**kwds_no_nanos)
else:
offset = timedelta(1)
offset = timedelta(0)
return offset, use_relativedelta


Expand Down Expand Up @@ -1068,12 +1069,17 @@ cdef class RelativeDeltaOffset(BaseOffset):
# perform calculation in UTC
other = other.replace(tzinfo=None)

if hasattr(self, "nanoseconds"):
td_nano = Timedelta(nanoseconds=self.nanoseconds)
else:
td_nano = Timedelta(0)

if self.n > 0:
for i in range(self.n):
other = other + self._offset
other = other + self._offset + td_nano
else:
for i in range(-self.n):
other = other - self._offset
other = other - self._offset - td_nano

if tzinfo is not None and self._use_relativedelta:
# bring tz back from UTC calculation
Expand Down
1 change: 0 additions & 1 deletion pandas/_libs/tslibs/timestamps.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,6 @@ cdef class _Timestamp(ABCTimestamp):
elif not isinstance(self, _Timestamp):
# cython semantics, args have been switched and this is __radd__
return other.__add__(self)

return NotImplemented

def __sub__(self, other):
Expand Down
64 changes: 56 additions & 8 deletions pandas/tests/tseries/offsets/test_offsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -668,14 +668,6 @@ def test_rule_code(self):
assert alias == (_get_offset(alias) * 5).rule_code


def test_dateoffset_misc():
oset = offsets.DateOffset(months=2, days=4)
# it works
oset.freqstr

assert not offsets.DateOffset(months=2) == 2


def test_freq_offsets():
off = BDay(1, offset=timedelta(0, 1800))
assert off.freqstr == "B+30Min"
Expand Down Expand Up @@ -791,6 +783,54 @@ def test_tick_normalize_raises(tick_classes):
cls(n=3, normalize=True)


@pytest.mark.parametrize(
"offset_kwargs, expected_arg",
[
({"nanoseconds": 1}, "1970-01-01 00:00:00.000000001"),
({"nanoseconds": 5}, "1970-01-01 00:00:00.000000005"),
({"nanoseconds": -1}, "1969-12-31 23:59:59.999999999"),
({"microseconds": 1}, "1970-01-01 00:00:00.000001"),
({"microseconds": -1}, "1969-12-31 23:59:59.999999"),
({"seconds": 1}, "1970-01-01 00:00:01"),
({"seconds": -1}, "1969-12-31 23:59:59"),
({"minutes": 1}, "1970-01-01 00:01:00"),
({"minutes": -1}, "1969-12-31 23:59:00"),
({"hours": 1}, "1970-01-01 01:00:00"),
({"hours": -1}, "1969-12-31 23:00:00"),
({"days": 1}, "1970-01-02 00:00:00"),
({"days": -1}, "1969-12-31 00:00:00"),
({"weeks": 1}, "1970-01-08 00:00:00"),
({"weeks": -1}, "1969-12-25 00:00:00"),
({"months": 1}, "1970-02-01 00:00:00"),
({"months": -1}, "1969-12-01 00:00:00"),
({"years": 1}, "1971-01-01 00:00:00"),
({"years": -1}, "1969-01-01 00:00:00"),
],
)
def test_dateoffset_add_sub(offset_kwargs, expected_arg):
offset = DateOffset(**offset_kwargs)
ts = Timestamp(0)
result = ts + offset
expected = Timestamp(expected_arg)
assert result == expected
result -= offset
assert result == ts
result = offset + ts
assert result == expected


def test_dataoffset_add_sub_timestamp_with_nano():
offset = DateOffset(minutes=2, nanoseconds=9)
ts = Timestamp(4)
result = ts + offset
expected = Timestamp("1970-01-01 00:02:00.000000013")
assert result == expected
result -= offset
assert result == ts
result = offset + ts
assert result == expected


@pytest.mark.parametrize(
"attribute",
[
Expand All @@ -806,3 +846,11 @@ def test_dateoffset_immutable(attribute):
msg = "DateOffset objects are immutable"
with pytest.raises(AttributeError, match=msg):
setattr(offset, attribute, 5)


def test_dateoffset_misc():
oset = offsets.DateOffset(months=2, days=4)
# it works
oset.freqstr

assert not offsets.DateOffset(months=2) == 2