Skip to content

Commit 9b3d648

Browse files
committed
BUG: catch overflow in timestamp + offset operations
xref statsmodels/statsmodels#3374
1 parent ab0d236 commit 9b3d648

File tree

5 files changed

+39
-6
lines changed

5 files changed

+39
-6
lines changed

doc/source/whatsnew/v0.20.0.txt

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

358+
- Bug in catching an overflow in ``Timestamp`` + ``Timedelta/Offset`` operations (:issue:``)
358359

359360

360361

pandas/tseries/offsets.py

+13-4
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,20 @@ 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+
result = as_timestamp(other).__add__(self)
2756+
if result == NotImplemented:
2757+
raise OverflowError
2758+
return result
2759+
elif isinstance(other, (datetime, np.datetime64, date)):
27522760
return as_timestamp(other) + self
2761+
27532762
if isinstance(other, timedelta):
2754-
return other + self.delta
2763+
return other.__add__(self.delta)
27552764
elif isinstance(other, type(self)):
27562765
return type(self)(self.n + other.n)
2757-
else:
2758-
raise ApplyTypeError('Unhandled type: %s' % type(other).__name__)
2766+
2767+
raise ApplyTypeError('Unhandled type: %s' % type(other).__name__)
27592768

27602769
_prefix = 'undefined'
27612770

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

+15
Original file line numberDiff line numberDiff line change
@@ -3320,6 +3320,21 @@ def test_datetime64_with_DateOffset(self):
33203320
# expected = rng + timedelta(2)
33213321
# self.assertTrue(result.equals(expected))
33223322

3323+
3324+
def test_really_large_offset(self):
3325+
# xref https://github.com/statsmodels/statsmodels/issues/3374
3326+
# ends up multiplying really large numbers which overflow
3327+
3328+
t = Timestamp('2017-01-13 00:00:00', freq='D')
3329+
offset = 20169940 * pd.offsets.Day(1)
3330+
def f():
3331+
t + offset
3332+
self.assertRaises(OverflowError, f)
3333+
3334+
def f():
3335+
offset + t
3336+
self.assertRaises(OverflowError, f)
3337+
33233338
def test_get_duplicates(self):
33243339
idx = DatetimeIndex(['2000-01-01', '2000-01-02', '2000-01-02',
33253340
'2000-01-03', '2000-01-03', '2000-01-04'])

pandas/tslib.pyx

+5-2
Original file line numberDiff line numberDiff line change
@@ -1220,7 +1220,10 @@ cdef class _Timestamp(datetime):
12201220
return Timestamp((self.freq * other).apply(self), freq=self.freq)
12211221

12221222
elif isinstance(other, timedelta) or hasattr(other, 'delta'):
1223-
nanos = _delta_to_nanoseconds(other)
1223+
try:
1224+
nanos = _delta_to_nanoseconds(other)
1225+
except OverflowError:
1226+
return NotImplemented
12241227
result = Timestamp(self.value + nanos,
12251228
tz=self.tzinfo, freq=self.freq)
12261229
if getattr(other, 'normalize', False):
@@ -3125,7 +3128,7 @@ class Timedelta(_Timedelta):
31253128
if not (is_integer_object(other) or is_float_object(other)):
31263129
return NotImplemented
31273130

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

31303133
__rmul__ = __mul__
31313134

0 commit comments

Comments
 (0)