Skip to content

Commit 9e5dfed

Browse files
jbrockmendelPingviinituutti
authored andcommitted
BUG: Fix date_range overflow (pandas-dev#23345)
1 parent 5775f81 commit 9e5dfed

File tree

3 files changed

+48
-2
lines changed

3 files changed

+48
-2
lines changed

doc/source/whatsnew/v0.24.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,7 @@ Datetimelike
10271027
- Bug in :class:`PeriodIndex` where adding or subtracting a :class:`timedelta` or :class:`Tick` object produced incorrect results (:issue:`22988`)
10281028
- Bug in :func:`date_range` when decrementing a start date to a past end date by a negative frequency (:issue:`23270`)
10291029
- Bug in :func:`DataFrame.combine` with datetimelike values raising a TypeError (:issue:`23079`)
1030+
- Bug in :func:`date_range` with frequency of ``Day`` or higher where dates sufficiently far in the future could wrap around to the past instead of raising ``OutOfBoundsDatetime`` (:issue:`14187`)
10301031

10311032
Timedelta
10321033
^^^^^^^^^

pandas/core/arrays/datetimes.py

+40-2
Original file line numberDiff line numberDiff line change
@@ -1355,11 +1355,11 @@ def _generate_regular_range(cls, start, end, periods, freq):
13551355
tz = start.tz
13561356
elif start is not None:
13571357
b = Timestamp(start).value
1358-
e = b + np.int64(periods) * stride
1358+
e = _generate_range_overflow_safe(b, periods, stride, side='start')
13591359
tz = start.tz
13601360
elif end is not None:
13611361
e = Timestamp(end).value + stride
1362-
b = e - np.int64(periods) * stride
1362+
b = _generate_range_overflow_safe(e, periods, stride, side='end')
13631363
tz = end.tz
13641364
else:
13651365
raise ValueError("at least 'start' or 'end' should be specified "
@@ -1384,6 +1384,44 @@ def _generate_regular_range(cls, start, end, periods, freq):
13841384
return data
13851385

13861386

1387+
def _generate_range_overflow_safe(endpoint, periods, stride, side='start'):
1388+
"""
1389+
Calculate the second endpoint for passing to np.arange, checking
1390+
to avoid an integer overflow. Catch OverflowError and re-raise
1391+
as OutOfBoundsDatetime.
1392+
1393+
Parameters
1394+
----------
1395+
endpoint : int
1396+
periods : int
1397+
stride : int
1398+
side : {'start', 'end'}
1399+
1400+
Returns
1401+
-------
1402+
other_end : int
1403+
1404+
Raises
1405+
------
1406+
OutOfBoundsDatetime
1407+
"""
1408+
# GH#14187 raise instead of incorrectly wrapping around
1409+
assert side in ['start', 'end']
1410+
if side == 'end':
1411+
stride *= -1
1412+
1413+
try:
1414+
other_end = checked_add_with_arr(np.int64(endpoint),
1415+
np.int64(periods) * stride)
1416+
except OverflowError:
1417+
raise tslib.OutOfBoundsDatetime('Cannot generate range with '
1418+
'{side}={endpoint} and '
1419+
'periods={periods}'
1420+
.format(side=side, endpoint=endpoint,
1421+
periods=periods))
1422+
return other_end
1423+
1424+
13871425
def _infer_tz_from_endpoints(start, end, tz):
13881426
"""
13891427
If a timezone is not explicitly given via `tz`, see if one can

pandas/tests/indexes/datetimes/test_date_range.py

+7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from pandas import (
1616
DatetimeIndex, Timestamp, bdate_range, compat, date_range, offsets
1717
)
18+
from pandas.errors import OutOfBoundsDatetime
1819
from pandas.tests.series.common import TestData
1920
from pandas.tseries.offsets import (
2021
BDay, CDay, DateOffset, MonthEnd, generate_range, prefix_mapping
@@ -79,6 +80,12 @@ def test_date_range_timestamp_equiv_preserve_frequency(self):
7980

8081

8182
class TestDateRanges(TestData):
83+
def test_date_range_out_of_bounds(self):
84+
# GH#14187
85+
with pytest.raises(OutOfBoundsDatetime):
86+
date_range('2016-01-01', periods=100000, freq='D')
87+
with pytest.raises(OutOfBoundsDatetime):
88+
date_range(end='1763-10-12', periods=100000, freq='D')
8289

8390
def test_date_range_gen_error(self):
8491
rng = date_range('1/1/2000 00:00', '1/1/2000 00:18', freq='5min')

0 commit comments

Comments
 (0)