Skip to content

Commit 0a7bb2a

Browse files
jbrockmendeljorisvandenbossche
authored andcommitted
BUG: fix to_timestamp out_of_bounds (#27916)
1 parent 9f71625 commit 0a7bb2a

File tree

4 files changed

+20
-11
lines changed

4 files changed

+20
-11
lines changed

doc/source/whatsnew/v0.25.1.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Categorical
3131
Datetimelike
3232
^^^^^^^^^^^^
3333
- Bug in :func:`to_datetime` where passing a timezone-naive :class:`DatetimeArray` or :class:`DatetimeIndex` and ``utc=True`` would incorrectly return a timezone-naive result (:issue:`27733`)
34-
-
34+
- Bug in :meth:`Period.to_timestamp` where a :class:`Period` outside the :class:`Timestamp` implementation bounds (roughly 1677-09-21 to 2262-04-11) would return an incorrect :class:`Timestamp` instead of raising ``OutOfBoundsDatetime`` (:issue:`19643`)
3535
-
3636
-
3737

pandas/_libs/tslibs/period.pyx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ PyDateTime_IMPORT
2121

2222
from pandas._libs.tslibs.np_datetime cimport (
2323
npy_datetimestruct, dtstruct_to_dt64, dt64_to_dtstruct,
24-
pandas_datetime_to_datetimestruct, NPY_DATETIMEUNIT, NPY_FR_D)
24+
pandas_datetime_to_datetimestruct, check_dts_bounds,
25+
NPY_DATETIMEUNIT, NPY_FR_D)
2526

2627
cdef extern from "src/datetime/np_datetime.h":
2728
int64_t npy_datetimestruct_to_datetime(NPY_DATETIMEUNIT fr,
@@ -1011,7 +1012,7 @@ def dt64arr_to_periodarr(int64_t[:] dtarr, int freq, tz=None):
10111012

10121013
@cython.wraparound(False)
10131014
@cython.boundscheck(False)
1014-
def periodarr_to_dt64arr(int64_t[:] periodarr, int freq):
1015+
def periodarr_to_dt64arr(const int64_t[:] periodarr, int freq):
10151016
"""
10161017
Convert array to datetime64 values from a set of ordinals corresponding to
10171018
periods per period convention.
@@ -1024,9 +1025,8 @@ def periodarr_to_dt64arr(int64_t[:] periodarr, int freq):
10241025

10251026
out = np.empty(l, dtype='i8')
10261027

1027-
with nogil:
1028-
for i in range(l):
1029-
out[i] = period_ordinal_to_dt64(periodarr[i], freq)
1028+
for i in range(l):
1029+
out[i] = period_ordinal_to_dt64(periodarr[i], freq)
10301030

10311031
return out.base # .base to access underlying np.ndarray
10321032

@@ -1179,14 +1179,15 @@ cpdef int64_t period_ordinal(int y, int m, int d, int h, int min,
11791179
return get_period_ordinal(&dts, freq)
11801180

11811181

1182-
cpdef int64_t period_ordinal_to_dt64(int64_t ordinal, int freq) nogil:
1182+
cdef int64_t period_ordinal_to_dt64(int64_t ordinal, int freq) except? -1:
11831183
cdef:
11841184
npy_datetimestruct dts
11851185

11861186
if ordinal == NPY_NAT:
11871187
return NPY_NAT
11881188

11891189
get_date_info(ordinal, freq, &dts)
1190+
check_dts_bounds(&dts)
11901191
return dtstruct_to_dt64(&dts)
11911192

11921193

pandas/tests/arrays/test_datetimelike.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import numpy as np
22
import pytest
33

4+
from pandas._libs import OutOfBoundsDatetime
5+
46
import pandas as pd
57
from pandas.core.arrays import DatetimeArray, PeriodArray, TimedeltaArray
68
import pandas.util.testing as tm
@@ -615,6 +617,15 @@ def test_to_timestamp(self, how, period_index):
615617
# an EA-specific tm.assert_ function
616618
tm.assert_index_equal(pd.Index(result), pd.Index(expected))
617619

620+
def test_to_timestamp_out_of_bounds(self):
621+
# GH#19643 previously overflowed silently
622+
pi = pd.period_range("1500", freq="Y", periods=3)
623+
with pytest.raises(OutOfBoundsDatetime):
624+
pi.to_timestamp()
625+
626+
with pytest.raises(OutOfBoundsDatetime):
627+
pi._data.to_timestamp()
628+
618629
@pytest.mark.parametrize("propname", PeriodArray._bool_ops)
619630
def test_bool_properties(self, period_index, propname):
620631
# in this case _bool_ops is just `is_leap_year`

pandas/tests/scalar/period/test_asfreq.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,8 @@ def test_asfreq_near_zero_weekly(self):
3030
assert week1.asfreq("D", "E") >= per1
3131
assert week2.asfreq("D", "S") <= per2
3232

33-
@pytest.mark.xfail(
34-
reason="GH#19643 period_helper asfreq functions fail to check for overflows"
35-
)
3633
def test_to_timestamp_out_of_bounds(self):
37-
# GH#19643, currently gives Timestamp('1754-08-30 22:43:41.128654848')
34+
# GH#19643, used to incorrectly give Timestamp in 1754
3835
per = Period("0001-01-01", freq="B")
3936
with pytest.raises(OutOfBoundsDatetime):
4037
per.to_timestamp()

0 commit comments

Comments
 (0)