Skip to content

Commit 4bd6a04

Browse files
meeseeksmachinejorisvandenbossche
authored andcommitted
Backport PR #27916: BUG: fix to_timestamp out_of_bounds (#28030)
1 parent c90243b commit 4bd6a04

File tree

4 files changed

+20
-11
lines changed

4 files changed

+20
-11
lines changed

doc/source/whatsnew/v0.25.1.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Categorical
3232
Datetimelike
3333
^^^^^^^^^^^^
3434
- 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`)
35-
-
35+
- 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`)
3636
-
3737
-
3838

pandas/_libs/tslibs/period.pyx

+7-6
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

+11
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
@@ -608,6 +610,15 @@ def test_to_timestamp(self, how, period_index):
608610
# an EA-specific tm.assert_ function
609611
tm.assert_index_equal(pd.Index(result), pd.Index(expected))
610612

613+
def test_to_timestamp_out_of_bounds(self):
614+
# GH#19643 previously overflowed silently
615+
pi = pd.period_range("1500", freq="Y", periods=3)
616+
with pytest.raises(OutOfBoundsDatetime):
617+
pi.to_timestamp()
618+
619+
with pytest.raises(OutOfBoundsDatetime):
620+
pi._data.to_timestamp()
621+
611622
@pytest.mark.parametrize("propname", PeriodArray._bool_ops)
612623
def test_bool_properties(self, period_index, propname):
613624
# in this case _bool_ops is just `is_leap_year`

pandas/tests/scalar/period/test_asfreq.py

+1-4
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)