Repository: pandas-dev/pandas

Issue:
date_range produces wrong values when overflowing
In the following case, you now get a RuntimeWarning. But shouldn't we rather error on this?

```
In [23]: pd.date_range('1/1/2000', periods=100000, freq='D')
/home/joris/scipy/pandas/pandas/tseries/index.py:1944: RuntimeWarning: overflow encountered in long_scalars
  e = b + np.int64(periods) * stride
/home/joris/scipy/pandas/pandas/tseries/index.py:1954: RuntimeWarning: overflow encountered in long_scalars
  data = np.arange(b, e, stride, dtype=np.int64)
Out[23]: 
DatetimeIndex([          '2000-01-01 00:00:00',
                         '2000-01-02 00:00:00',
                         '2000-01-03 00:00:00',
                         '2000-01-04 00:00:00',
                         '2000-01-05 00:00:00',
                         '2000-01-06 00:00:00',
                         '2000-01-07 00:00:00',
                         '2000-01-08 00:00:00',
                         '2000-01-09 00:00:00',
                         '2000-01-10 00:00:00',
               ...
               '1689-03-17 00:25:26.290448384',
               '1689-03-18 00:25:26.290448384',
               '1689-03-19 00:25:26.290448384',
               '1689-03-20 00:25:26.290448384',
               '1689-03-21 00:25:26.290448384',
               '1689-03-22 00:25:26.290448384',
               '1689-03-23 00:25:26.290448384',
               '1689-03-24 00:25:26.290448384',
               '1689-03-25 00:25:26.290448384',
               '1689-03-26 00:25:26.290448384'],
              dtype='datetime64[ns]', length=100000, freq='D')

In [24]: pd.__version__
Out[24]: '0.19.0rc1+2.gd8cd33b'

In [25]: np.__version__
Out[25]: '1.11.1'

```



Discussion:
might be now showing because of the `np.errstate` changes. yes an overflow is not good here (in general we can/should catch the overflow in an offset and just return a `datetime`, rather than a `Timestamp`) But here I think its an error as we can't represent that range.

something like this would work I think

```
try:
    with np.errstate(over='raise'):
         e = b + .....
except FloatingPointError:
    raise ValueError("cannot create a date range like that")
```


Resolving patch:
diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py
--- a/pandas/core/arrays/datetimes.py
+++ b/pandas/core/arrays/datetimes.py
@@ -1355,11 +1355,11 @@ def _generate_regular_range(cls, start, end, periods, freq):
             tz = start.tz
         elif start is not None:
             b = Timestamp(start).value
-            e = b + np.int64(periods) * stride
+            e = _generate_range_overflow_safe(b, periods, stride, side='start')
             tz = start.tz
         elif end is not None:
             e = Timestamp(end).value + stride
-            b = e - np.int64(periods) * stride
+            b = _generate_range_overflow_safe(e, periods, stride, side='end')
             tz = end.tz
         else:
             raise ValueError("at least 'start' or 'end' should be specified "
@@ -1384,6 +1384,44 @@ def _generate_regular_range(cls, start, end, periods, freq):
     return data
 
 
+def _generate_range_overflow_safe(endpoint, periods, stride, side='start'):
+    """
+    Calculate the second endpoint for passing to np.arange, checking
+    to avoid an integer overflow.  Catch OverflowError and re-raise
+    as OutOfBoundsDatetime.
+
+    Parameters
+    ----------
+    endpoint : int
+    periods : int
+    stride : int
+    side : {'start', 'end'}
+
+    Returns
+    -------
+    other_end : int
+
+    Raises
+    ------
+    OutOfBoundsDatetime
+    """
+    # GH#14187 raise instead of incorrectly wrapping around
+    assert side in ['start', 'end']
+    if side == 'end':
+        stride *= -1
+
+    try:
+        other_end = checked_add_with_arr(np.int64(endpoint),
+                                         np.int64(periods) * stride)
+    except OverflowError:
+        raise tslib.OutOfBoundsDatetime('Cannot generate range with '
+                                        '{side}={endpoint} and '
+                                        'periods={periods}'
+                                        .format(side=side, endpoint=endpoint,
+                                                periods=periods))
+    return other_end
+
+
 def _infer_tz_from_endpoints(start, end, tz):
     """
     If a timezone is not explicitly given via `tz`, see if one can


Test patch:
diff --git a/pandas/tests/indexes/datetimes/test_date_range.py b/pandas/tests/indexes/datetimes/test_date_range.py
--- a/pandas/tests/indexes/datetimes/test_date_range.py
+++ b/pandas/tests/indexes/datetimes/test_date_range.py
@@ -15,6 +15,7 @@
 from pandas import (
     DatetimeIndex, Timestamp, bdate_range, compat, date_range, offsets
 )
+from pandas.errors import OutOfBoundsDatetime
 from pandas.tests.series.common import TestData
 from pandas.tseries.offsets import (
     BDay, CDay, DateOffset, MonthEnd, generate_range, prefix_mapping
@@ -79,6 +80,12 @@ def test_date_range_timestamp_equiv_preserve_frequency(self):
 
 
 class TestDateRanges(TestData):
+    def test_date_range_out_of_bounds(self):
+        # GH#14187
+        with pytest.raises(OutOfBoundsDatetime):
+            date_range('2016-01-01', periods=100000, freq='D')
+        with pytest.raises(OutOfBoundsDatetime):
+            date_range(end='1763-10-12', periods=100000, freq='D')
 
     def test_date_range_gen_error(self):
         rng = date_range('1/1/2000 00:00', '1/1/2000 00:18', freq='5min')