diff --git a/pandas/_libs/tslibs/__init__.py b/pandas/_libs/tslibs/__init__.py index 4c74959fee60d..15836854bad4d 100644 --- a/pandas/_libs/tslibs/__init__.py +++ b/pandas/_libs/tslibs/__init__.py @@ -27,10 +27,7 @@ ] from pandas._libs.tslibs import dtypes -from pandas._libs.tslibs.conversion import ( - OutOfBoundsTimedelta, - localize_pydatetime, -) +from pandas._libs.tslibs.conversion import localize_pydatetime from pandas._libs.tslibs.dtypes import Resolution from pandas._libs.tslibs.nattype import ( NaT, @@ -38,7 +35,10 @@ iNaT, nat_strings, ) -from pandas._libs.tslibs.np_datetime import OutOfBoundsDatetime +from pandas._libs.tslibs.np_datetime import ( + OutOfBoundsDatetime, + OutOfBoundsTimedelta, +) from pandas._libs.tslibs.offsets import ( BaseOffset, Tick, diff --git a/pandas/_libs/tslibs/conversion.pyx b/pandas/_libs/tslibs/conversion.pyx index e4b0c527a4cac..457b27b293f11 100644 --- a/pandas/_libs/tslibs/conversion.pyx +++ b/pandas/_libs/tslibs/conversion.pyx @@ -44,7 +44,10 @@ from pandas._libs.tslibs.np_datetime cimport ( pydatetime_to_dt64, ) -from pandas._libs.tslibs.np_datetime import OutOfBoundsDatetime +from pandas._libs.tslibs.np_datetime import ( + OutOfBoundsDatetime, + OutOfBoundsTimedelta, +) from pandas._libs.tslibs.timezones cimport ( get_dst_info, @@ -85,15 +88,6 @@ DT64NS_DTYPE = np.dtype('M8[ns]') TD64NS_DTYPE = np.dtype('m8[ns]') -class OutOfBoundsTimedelta(ValueError): - """ - Raised when encountering a timedelta value that cannot be represented - as a timedelta64[ns]. - """ - # Timedelta analogue to OutOfBoundsDatetime - pass - - # ---------------------------------------------------------------------- # Unit Conversion Helpers diff --git a/pandas/_libs/tslibs/np_datetime.pxd b/pandas/_libs/tslibs/np_datetime.pxd index 211b47cc3dc20..fb8ae4aae0e7b 100644 --- a/pandas/_libs/tslibs/np_datetime.pxd +++ b/pandas/_libs/tslibs/np_datetime.pxd @@ -66,6 +66,10 @@ cdef extern from "src/datetime/np_datetime.h": npy_datetime npy_datetimestruct_to_datetime(NPY_DATETIMEUNIT fr, npy_datetimestruct *d) nogil + void pandas_timedelta_to_timedeltastruct(npy_timedelta val, + NPY_DATETIMEUNIT fr, + pandas_timedeltastruct *result + ) nogil cdef bint cmp_scalar(int64_t lhs, int64_t rhs, int op) except -1 @@ -94,3 +98,5 @@ cpdef cnp.ndarray astype_overflowsafe( cnp.dtype dtype, # ndarray[datetime64[anyunit]] bint copy=*, ) + +cdef bint cmp_dtstructs(npy_datetimestruct* left, npy_datetimestruct* right, int op) diff --git a/pandas/_libs/tslibs/np_datetime.pyi b/pandas/_libs/tslibs/np_datetime.pyi index 922fe8c4a4dce..59f4427125266 100644 --- a/pandas/_libs/tslibs/np_datetime.pyi +++ b/pandas/_libs/tslibs/np_datetime.pyi @@ -1,6 +1,7 @@ import numpy as np class OutOfBoundsDatetime(ValueError): ... +class OutOfBoundsTimedelta(ValueError): ... # only exposed for testing def py_get_unit_from_dtype(dtype: np.dtype): ... diff --git a/pandas/_libs/tslibs/np_datetime.pyx b/pandas/_libs/tslibs/np_datetime.pyx index 2a0b4ceeb98f8..44646b0d4e87f 100644 --- a/pandas/_libs/tslibs/np_datetime.pyx +++ b/pandas/_libs/tslibs/np_datetime.pyx @@ -34,11 +34,6 @@ cdef extern from "src/datetime/np_datetime.h": int cmp_npy_datetimestruct(npy_datetimestruct *a, npy_datetimestruct *b) - void pandas_timedelta_to_timedeltastruct(npy_timedelta val, - NPY_DATETIMEUNIT fr, - pandas_timedeltastruct *result - ) nogil - # AS, FS, PS versions exist but are not imported because they are not used. npy_datetimestruct _NS_MIN_DTS, _NS_MAX_DTS npy_datetimestruct _US_MIN_DTS, _US_MAX_DTS @@ -100,6 +95,28 @@ def py_get_unit_from_dtype(dtype): # Comparison +cdef bint cmp_dtstructs( + npy_datetimestruct* left, npy_datetimestruct* right, int op +): + cdef: + int cmp_res + + cmp_res = cmp_npy_datetimestruct(left, right) + if op == Py_EQ: + return cmp_res == 0 + if op == Py_NE: + return cmp_res != 0 + if op == Py_GT: + return cmp_res == 1 + if op == Py_LT: + return cmp_res == -1 + if op == Py_GE: + return cmp_res == 1 or cmp_res == 0 + else: + # i.e. op == Py_LE + return cmp_res == -1 or cmp_res == 0 + + cdef inline bint cmp_scalar(int64_t lhs, int64_t rhs, int op) except -1: """ cmp_scalar is a more performant version of PyObject_RichCompare @@ -127,6 +144,15 @@ class OutOfBoundsDatetime(ValueError): pass +class OutOfBoundsTimedelta(ValueError): + """ + Raised when encountering a timedelta value that cannot be represented + as a timedelta64[ns]. + """ + # Timedelta analogue to OutOfBoundsDatetime + pass + + cdef check_dts_bounds(npy_datetimestruct *dts, NPY_DATETIMEUNIT unit=NPY_FR_ns): """Raises OutOfBoundsDatetime if the given date is outside the range that can be represented by nanosecond-resolution 64-bit integers.""" diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index 627006a7f32c0..b443ae283755c 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -51,6 +51,7 @@ from pandas._libs.tslibs.np_datetime cimport ( pandas_timedeltastruct, td64_to_tdstruct, ) +from pandas._libs.tslibs.np_datetime import OutOfBoundsTimedelta from pandas._libs.tslibs.offsets cimport is_tick_object from pandas._libs.tslibs.util cimport ( is_array, @@ -188,7 +189,6 @@ cpdef int64_t delta_to_nanoseconds(delta) except? -1: + delta.microseconds ) * 1000 except OverflowError as err: - from pandas._libs.tslibs.conversion import OutOfBoundsTimedelta raise OutOfBoundsTimedelta(*err.args) from err raise TypeError(type(delta)) @@ -226,7 +226,6 @@ cdef object ensure_td64ns(object ts): # NB: cython#1381 this cannot be *= td64_value = td64_value * mult except OverflowError as err: - from pandas._libs.tslibs.conversion import OutOfBoundsTimedelta raise OutOfBoundsTimedelta(ts) from err return np.timedelta64(td64_value, "ns")