Skip to content

Commit 7892077

Browse files
committed
BUG: catch overflow in timestamp + offset operations
xref statsmodels/statsmodels#3374 Author: Jeff Reback <[email protected]> Closes #15126 from jreback/inc and squashes the following commits: 923eaa2 [Jeff Reback] BUG: catch overflow in timestamp + offset operations
1 parent e6a3c35 commit 7892077

File tree

5 files changed

+43
-10
lines changed

5 files changed

+43
-10
lines changed

doc/source/whatsnew/v0.20.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ Bug Fixes
356356
- Bug in ``DataFrame.groupby().describe()`` when grouping on ``Index`` containing tuples (:issue:`14848`)
357357
- Bug in creating a ``MultiIndex`` with tuples and not passing a list of names; this will now raise ``ValueError`` (:issue:`15110`)
358358

359+
- Bug in catching an overflow in ``Timestamp`` + ``Timedelta/Offset`` operations (:issue:`15126`)
359360

360361

361362

pandas/tseries/offsets.py

+18-3
Original file line numberDiff line numberDiff line change
@@ -2710,6 +2710,9 @@ def __add__(self, other):
27102710
return self.apply(other)
27112711
except ApplyTypeError:
27122712
return NotImplemented
2713+
except OverflowError:
2714+
raise OverflowError("the add operation between {} and {} "
2715+
"will overflow".format(self, other))
27132716

27142717
def __eq__(self, other):
27152718
if isinstance(other, compat.string_types):
@@ -2748,14 +2751,26 @@ def nanos(self):
27482751

27492752
def apply(self, other):
27502753
# Timestamp can handle tz and nano sec, thus no need to use apply_wraps
2751-
if isinstance(other, (datetime, np.datetime64, date)):
2754+
if isinstance(other, Timestamp):
2755+
2756+
# GH 15126
2757+
# in order to avoid a recursive
2758+
# call of __add__ and __radd__ if there is
2759+
# an exception, when we call using the + operator,
2760+
# we directly call the known method
2761+
result = other.__add__(self)
2762+
if result == NotImplemented:
2763+
raise OverflowError
2764+
return result
2765+
elif isinstance(other, (datetime, np.datetime64, date)):
27522766
return as_timestamp(other) + self
2767+
27532768
if isinstance(other, timedelta):
27542769
return other + self.delta
27552770
elif isinstance(other, type(self)):
27562771
return type(self)(self.n + other.n)
2757-
else:
2758-
raise ApplyTypeError('Unhandled type: %s' % type(other).__name__)
2772+
2773+
raise ApplyTypeError('Unhandled type: %s' % type(other).__name__)
27592774

27602775
_prefix = 'undefined'
27612776

pandas/tseries/tests/test_timedeltas.py

+5
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,11 @@ def test_overflow(self):
971971
s2 = s[0:1000]
972972
result = (s2 - s2.min()).sum()
973973

974+
def test_overflow_on_construction(self):
975+
# xref https://github.com/statsmodels/statsmodels/issues/3374
976+
value = pd.Timedelta('1day').value * 20169940
977+
self.assertRaises(OverflowError, pd.Timedelta, value)
978+
974979
def test_timedelta_ops_scalar(self):
975980
# GH 6808
976981
base = pd.to_datetime('20130101 09:01:12.123456')

pandas/tseries/tests/test_timeseries.py

+18-6
Original file line numberDiff line numberDiff line change
@@ -3312,13 +3312,25 @@ def test_datetime64_with_DateOffset(self):
33123312
assert_func(klass([x + op for x in s]), s + op)
33133313
assert_func(klass([x - op for x in s]), s - op)
33143314
assert_func(klass([op + x for x in s]), op + s)
3315-
# def test_add_timedelta64(self):
3316-
# rng = date_range('1/1/2000', periods=5)
3317-
# delta = rng.values[3] - rng.values[1]
33183315

3319-
# result = rng + delta
3320-
# expected = rng + timedelta(2)
3321-
# self.assertTrue(result.equals(expected))
3316+
def test_overflow_offset(self):
3317+
# xref https://github.com/statsmodels/statsmodels/issues/3374
3318+
# ends up multiplying really large numbers which overflow
3319+
3320+
t = Timestamp('2017-01-13 00:00:00', freq='D')
3321+
offset = 20169940 * pd.offsets.Day(1)
3322+
3323+
def f():
3324+
t + offset
3325+
self.assertRaises(OverflowError, f)
3326+
3327+
def f():
3328+
offset + t
3329+
self.assertRaises(OverflowError, f)
3330+
3331+
def f():
3332+
t - offset
3333+
self.assertRaises(OverflowError, f)
33223334

33233335
def test_get_duplicates(self):
33243336
idx = DatetimeIndex(['2000-01-01', '2000-01-02', '2000-01-02',

pandas/tslib.pyx

+1-1
Original file line numberDiff line numberDiff line change
@@ -3125,7 +3125,7 @@ class Timedelta(_Timedelta):
31253125
if not (is_integer_object(other) or is_float_object(other)):
31263126
return NotImplemented
31273127

3128-
return Timedelta(other *self.value, unit='ns')
3128+
return Timedelta(other * self.value, unit='ns')
31293129

31303130
__rmul__ = __mul__
31313131

0 commit comments

Comments
 (0)