From b6b0af16e6cf7690fc5f026f18044ac48c86e770 Mon Sep 17 00:00:00 2001 From: pavansai018 <18pavansai@gmail.com> Date: Thu, 11 Dec 2025 15:03:45 +0800 Subject: [PATCH 1/2] BUG: Raise OutOfBoundsDatetime when Period is constructed from invalid high-resolution Timestamp (#63278) --- pandas/_libs/tslibs/period.pyx | 42 +++++++++++++++++++++++++++++- pandas/tests/tslibs/test_period.py | 10 ++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/pandas/_libs/tslibs/period.pyx b/pandas/_libs/tslibs/period.pyx index fd905248c4558..4b3b8d18fc697 100644 --- a/pandas/_libs/tslibs/period.pyx +++ b/pandas/_libs/tslibs/period.pyx @@ -812,6 +812,45 @@ cdef int64_t get_period_ordinal(npy_datetimestruct *dts, int freq) noexcept nogi unit = freq_group_code_to_npy_unit(freq) return npy_datetimestruct_to_datetime(unit, dts) +cdef int64_t _period_ordinal_safe(npy_datetimestruct *dts, int freq) except? -1: + """ + Safe variant of get_period_ordinal used by the Python API (period_ordinal). + + It mirrors get_period_ordinal's logic but is allowed to raise Python + exceptions instead of leaking OverflowError from numpy's datetime code. + """ + cdef: + int64_t unix_date + int freq_group, fmonth + NPY_DATETIMEUNIT unit + + freq_group = get_freq_group(freq) + + try: + if freq_group == FR_ANN: + fmonth = get_anchor_month(freq, freq_group) + return dts_to_year_ordinal(dts, fmonth) + + elif freq_group == FR_QTR: + fmonth = get_anchor_month(freq, freq_group) + return dts_to_qtr_ordinal(dts, fmonth) + + elif freq_group == FR_WK: + unix_date = npy_datetimestruct_to_datetime(NPY_FR_D, dts) + return unix_date_to_week(unix_date, freq - FR_WK) + + elif freq == FR_BUS: + unix_date = npy_datetimestruct_to_datetime(NPY_FR_D, dts) + return DtoB(dts, 0, unix_date) + + unit = freq_group_code_to_npy_unit(freq) + return npy_datetimestruct_to_datetime(unit, dts) + + except OverflowError as err: + # Translate low-level overflow into a user-facing OutOfBoundsDatetime. + fmt = dts_to_iso_string(dts) + raise OutOfBoundsDatetime(f"Out of bounds datetime for Period with freq {freq}: {fmt}") from err + cdef void get_date_info(int64_t ordinal, int freq, npy_datetimestruct *dts) noexcept nogil: @@ -1150,7 +1189,8 @@ cpdef int64_t period_ordinal(int y, int m, int d, int h, int min, dts.sec = s dts.us = us dts.ps = ps - return get_period_ordinal(&dts, freq) + #return get_period_ordinal(&dts, freq) + return _period_ordinal_safe(&dts, freq) cdef int64_t period_ordinal_to_dt64(int64_t ordinal, int freq) except? -1: diff --git a/pandas/tests/tslibs/test_period.py b/pandas/tests/tslibs/test_period.py index 715e2d3da88db..fd009f1a6848e 100644 --- a/pandas/tests/tslibs/test_period.py +++ b/pandas/tests/tslibs/test_period.py @@ -1,9 +1,10 @@ import numpy as np import pytest - +import pandas as pd from pandas._libs.tslibs import ( iNaT, to_offset, + OutOfBoundsDatetime, ) from pandas._libs.tslibs.period import ( extract_ordinals, @@ -121,3 +122,10 @@ def test_get_period_field_array_raises_on_out_of_range(): msg = "Buffer dtype mismatch, expected 'const int64_t' but got 'double'" with pytest.raises(ValueError, match=msg): get_period_field_arr(-1, np.empty(1), 0) + +def test_period_from_overflow_timestamp_raises(): + # Construct a deliberately broken Timestamp + ts = pd.Timestamp(pd.Timestamp.min.value, unit="us") + + with pytest.raises(OutOfBoundsDatetime): + pd.Period(ts, freq="us") \ No newline at end of file From 26b2e027dbc91566c007dd98e602695c707c263a Mon Sep 17 00:00:00 2001 From: pavansai018 <18pavansai@gmail.com> Date: Thu, 11 Dec 2025 16:12:58 +0800 Subject: [PATCH 2/2] BUG: Raise OutOfBoundsDatetime when Period is constructed from invalid high-resolution Timestamp (#63278) --- pandas/_libs/tslibs/period.pyx | 6 ++++-- pandas/tests/tslibs/test_period.py | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pandas/_libs/tslibs/period.pyx b/pandas/_libs/tslibs/period.pyx index 4b3b8d18fc697..82a17a6c42018 100644 --- a/pandas/_libs/tslibs/period.pyx +++ b/pandas/_libs/tslibs/period.pyx @@ -849,7 +849,9 @@ cdef int64_t _period_ordinal_safe(npy_datetimestruct *dts, int freq) except? -1: except OverflowError as err: # Translate low-level overflow into a user-facing OutOfBoundsDatetime. fmt = dts_to_iso_string(dts) - raise OutOfBoundsDatetime(f"Out of bounds datetime for Period with freq {freq}: {fmt}") from err + raise OutOfBoundsDatetime( + f"Out of bounds datetime for Period with freq {freq}: {fmt}" + ) from err cdef void get_date_info(int64_t ordinal, @@ -1189,7 +1191,7 @@ cpdef int64_t period_ordinal(int y, int m, int d, int h, int min, dts.sec = s dts.us = us dts.ps = ps - #return get_period_ordinal(&dts, freq) + # return get_period_ordinal(&dts, freq) return _period_ordinal_safe(&dts, freq) diff --git a/pandas/tests/tslibs/test_period.py b/pandas/tests/tslibs/test_period.py index fd009f1a6848e..34beee91c8115 100644 --- a/pandas/tests/tslibs/test_period.py +++ b/pandas/tests/tslibs/test_period.py @@ -1,10 +1,10 @@ import numpy as np import pytest -import pandas as pd + from pandas._libs.tslibs import ( + OutOfBoundsDatetime, iNaT, to_offset, - OutOfBoundsDatetime, ) from pandas._libs.tslibs.period import ( extract_ordinals, @@ -13,6 +13,7 @@ period_ordinal, ) +import pandas as pd import pandas._testing as tm @@ -123,9 +124,10 @@ def test_get_period_field_array_raises_on_out_of_range(): with pytest.raises(ValueError, match=msg): get_period_field_arr(-1, np.empty(1), 0) + def test_period_from_overflow_timestamp_raises(): # Construct a deliberately broken Timestamp ts = pd.Timestamp(pd.Timestamp.min.value, unit="us") with pytest.raises(OutOfBoundsDatetime): - pd.Period(ts, freq="us") \ No newline at end of file + pd.Period(ts, freq="us")