diff --git a/pandas/_libs/src/datetime/np_datetime.c b/pandas/_libs/src/datetime/np_datetime.c index 1ad8c780ba7a4..9e56802b92bf0 100644 --- a/pandas/_libs/src/datetime/np_datetime.c +++ b/pandas/_libs/src/datetime/np_datetime.c @@ -235,8 +235,7 @@ NPY_NO_EXPORT void add_seconds_to_datetimestruct(npy_datetimestruct *dts, * Fills in the year, month, day in 'dts' based on the days * offset from 1970. */ -static void set_datetimestruct_days(npy_int64 days, - npy_datetimestruct *dts) { +static void set_datetimestruct_days(npy_int64 days, npy_datetimestruct *dts) { const int *month_lengths; int i; @@ -318,7 +317,7 @@ int cmp_npy_datetimestruct(const npy_datetimestruct *a, /* * - * Tests for and converts a Python datetime.datetime or datetime.date + * Converts a Python datetime.datetime or datetime.date * object into a NumPy npy_datetimestruct. Uses tzinfo (if present) * to convert to UTC time. * @@ -330,68 +329,22 @@ int cmp_npy_datetimestruct(const npy_datetimestruct *a, * Returns -1 on error, 0 on success, and 1 (with no error set) * if obj doesn't have the needed date or datetime attributes. */ -int convert_pydatetime_to_datetimestruct(PyObject *obj, +int convert_pydatetime_to_datetimestruct(PyDateTime_Date *obj, npy_datetimestruct *out) { + // Assumes that obj is a valid datetime object PyObject *tmp; - int isleap; /* Initialize the output to all zeros */ memset(out, 0, sizeof(npy_datetimestruct)); out->month = 1; out->day = 1; - /* Need at least year/month/day attributes */ - if (!PyObject_HasAttrString(obj, "year") || - !PyObject_HasAttrString(obj, "month") || - !PyObject_HasAttrString(obj, "day")) { - return 1; - } - - /* Get the year */ - tmp = PyObject_GetAttrString(obj, "year"); - if (tmp == NULL) { - return -1; - } - out->year = PyInt_AsLong(tmp); - if (out->year == -1 && PyErr_Occurred()) { - Py_DECREF(tmp); - return -1; - } - Py_DECREF(tmp); - - /* Get the month */ - tmp = PyObject_GetAttrString(obj, "month"); - if (tmp == NULL) { - return -1; - } - out->month = PyInt_AsLong(tmp); - if (out->month == -1 && PyErr_Occurred()) { - Py_DECREF(tmp); - return -1; - } - Py_DECREF(tmp); - - /* Get the day */ - tmp = PyObject_GetAttrString(obj, "day"); - if (tmp == NULL) { - return -1; - } - out->day = PyInt_AsLong(tmp); - if (out->day == -1 && PyErr_Occurred()) { - Py_DECREF(tmp); - return -1; - } - Py_DECREF(tmp); + out->year = PyInt_AsLong(PyObject_GetAttrString(obj, "year")); + out->month = PyInt_AsLong(PyObject_GetAttrString(obj, "month")); + out->day = PyInt_AsLong(PyObject_GetAttrString(obj, "day")); - /* Validate that the month and day are valid for the year */ - if (out->month < 1 || out->month > 12) { - goto invalid_date; - } - isleap = is_leapyear(out->year); - if (out->day < 1 || - out->day > days_per_month_table[isleap][out->month - 1]) { - goto invalid_date; - } + // TODO(anyone): If we can get PyDateTime_IMPORT to work, we could use + // PyDateTime_Check here, and less verbose attribute lookups. /* Check for time attributes (if not there, return success as a date) */ if (!PyObject_HasAttrString(obj, "hour") || @@ -401,61 +354,13 @@ int convert_pydatetime_to_datetimestruct(PyObject *obj, return 0; } - /* Get the hour */ - tmp = PyObject_GetAttrString(obj, "hour"); - if (tmp == NULL) { - return -1; - } - out->hour = PyInt_AsLong(tmp); - if (out->hour == -1 && PyErr_Occurred()) { - Py_DECREF(tmp); - return -1; - } - Py_DECREF(tmp); - - /* Get the minute */ - tmp = PyObject_GetAttrString(obj, "minute"); - if (tmp == NULL) { - return -1; - } - out->min = PyInt_AsLong(tmp); - if (out->min == -1 && PyErr_Occurred()) { - Py_DECREF(tmp); - return -1; - } - Py_DECREF(tmp); - - /* Get the second */ - tmp = PyObject_GetAttrString(obj, "second"); - if (tmp == NULL) { - return -1; - } - out->sec = PyInt_AsLong(tmp); - if (out->sec == -1 && PyErr_Occurred()) { - Py_DECREF(tmp); - return -1; - } - Py_DECREF(tmp); + out->hour = PyInt_AsLong(PyObject_GetAttrString(obj, "hour")); + out->min = PyInt_AsLong(PyObject_GetAttrString(obj, "minute")); + out->sec = PyInt_AsLong(PyObject_GetAttrString(obj, "second")); + out->us = PyInt_AsLong(PyObject_GetAttrString(obj, "microsecond")); - /* Get the microsecond */ - tmp = PyObject_GetAttrString(obj, "microsecond"); - if (tmp == NULL) { - return -1; - } - out->us = PyInt_AsLong(tmp); - if (out->us == -1 && PyErr_Occurred()) { - Py_DECREF(tmp); - return -1; - } - Py_DECREF(tmp); - - if (out->hour < 0 || out->hour >= 24 || out->min < 0 || out->min >= 60 || - out->sec < 0 || out->sec >= 60 || out->us < 0 || out->us >= 1000000) { - goto invalid_time; - } - - /* Apply the time zone offset if it exists */ - if (PyObject_HasAttrString(obj, "tzinfo")) { + /* Apply the time zone offset if datetime obj is tz-aware */ + if (PyObject_HasAttrString((PyObject*)obj, "tzinfo")) { tmp = PyObject_GetAttrString(obj, "tzinfo"); if (tmp == NULL) { return -1; @@ -497,50 +402,15 @@ int convert_pydatetime_to_datetimestruct(PyObject *obj, } return 0; - -invalid_date: - PyErr_Format(PyExc_ValueError, - "Invalid date (%d,%d,%d) when converting to NumPy datetime", - (int)out->year, (int)out->month, (int)out->day); - return -1; - -invalid_time: - PyErr_Format(PyExc_ValueError, - "Invalid time (%d,%d,%d,%d) when converting " - "to NumPy datetime", - (int)out->hour, (int)out->min, (int)out->sec, (int)out->us); - return -1; -} - -npy_datetime npy_datetimestruct_to_datetime(NPY_DATETIMEUNIT fr, - npy_datetimestruct *d) { - npy_datetime result = NPY_DATETIME_NAT; - - convert_datetimestruct_to_datetime(fr, d, &result); - return result; -} - -void pandas_datetime_to_datetimestruct(npy_datetime val, NPY_DATETIMEUNIT fr, - npy_datetimestruct *result) { - convert_datetime_to_datetimestruct(fr, val, result); -} - -void pandas_timedelta_to_timedeltastruct(npy_timedelta val, - NPY_DATETIMEUNIT fr, - pandas_timedeltastruct *result) { - convert_timedelta_to_timedeltastruct(fr, val, result); } /* * Converts a datetime from a datetimestruct to a datetime based * on a metadata unit. The date is assumed to be valid. - * - * Returns 0 on success, -1 on failure. */ -int convert_datetimestruct_to_datetime(NPY_DATETIMEUNIT base, - const npy_datetimestruct *dts, - npy_datetime *out) { +npy_datetime npy_datetimestruct_to_datetime(NPY_DATETIMEUNIT base, + const npy_datetimestruct *dts) { npy_datetime ret; if (base == NPY_FR_Y) { @@ -632,17 +502,14 @@ int convert_datetimestruct_to_datetime(NPY_DATETIMEUNIT base, return -1; } } - - *out = ret; - - return 0; + return ret; } /* * Converts a datetime based on the given metadata into a datetimestruct */ -int convert_datetime_to_datetimestruct(NPY_DATETIMEUNIT base, - npy_datetime dt, +void pandas_datetime_to_datetimestruct(npy_datetime dt, + NPY_DATETIMEUNIT base, npy_datetimestruct *out) { npy_int64 perday; @@ -850,10 +717,7 @@ int convert_datetime_to_datetimestruct(NPY_DATETIMEUNIT base, PyErr_SetString(PyExc_RuntimeError, "NumPy datetime metadata is corrupted with invalid " "base unit"); - return -1; } - - return 0; } /* @@ -862,8 +726,8 @@ int convert_datetime_to_datetimestruct(NPY_DATETIMEUNIT base, * * Returns 0 on success, -1 on failure. */ -int convert_timedelta_to_timedeltastruct(NPY_DATETIMEUNIT base, - npy_timedelta td, +void pandas_timedelta_to_timedeltastruct(npy_timedelta td, + NPY_DATETIMEUNIT base, pandas_timedeltastruct *out) { npy_int64 frac; npy_int64 sfrac; @@ -953,8 +817,5 @@ int convert_timedelta_to_timedeltastruct(NPY_DATETIMEUNIT base, PyErr_SetString(PyExc_RuntimeError, "NumPy timedelta metadata is corrupted with " "invalid base unit"); - return -1; } - - return 0; } diff --git a/pandas/_libs/src/datetime/np_datetime.h b/pandas/_libs/src/datetime/np_datetime.h index f5c48036c16f8..4347d0c8c47d4 100644 --- a/pandas/_libs/src/datetime/np_datetime.h +++ b/pandas/_libs/src/datetime/np_datetime.h @@ -18,6 +18,7 @@ This file is derived from NumPy 1.7. See NUMPY_LICENSE.txt #define PANDAS__LIBS_SRC_DATETIME_NP_DATETIME_H_ #include +#include typedef struct { npy_int64 days; @@ -30,11 +31,11 @@ extern const npy_datetimestruct _NS_MAX_DTS; // stuff pandas needs // ---------------------------------------------------------------------------- -int convert_pydatetime_to_datetimestruct(PyObject *obj, +int convert_pydatetime_to_datetimestruct(PyDateTime_Date *obj, npy_datetimestruct *out); -npy_datetime npy_datetimestruct_to_datetime(NPY_DATETIMEUNIT fr, - npy_datetimestruct *d); +npy_datetime npy_datetimestruct_to_datetime(NPY_DATETIMEUNIT base, + const npy_datetimestruct *dts); void pandas_datetime_to_datetimestruct(npy_datetime val, NPY_DATETIMEUNIT fr, npy_datetimestruct *result); @@ -74,9 +75,4 @@ void add_minutes_to_datetimestruct(npy_datetimestruct *dts, int minutes); -int -convert_datetime_to_datetimestruct(NPY_DATETIMEUNIT base, - npy_datetime dt, - npy_datetimestruct *out); - #endif // PANDAS__LIBS_SRC_DATETIME_NP_DATETIME_H_ diff --git a/pandas/_libs/src/ujson/python/objToJSON.c b/pandas/_libs/src/ujson/python/objToJSON.c index c9b0143ffc6ca..4bab32e93ab1e 100644 --- a/pandas/_libs/src/ujson/python/objToJSON.c +++ b/pandas/_libs/src/ujson/python/objToJSON.c @@ -481,16 +481,17 @@ static void *NpyDateTimeScalarToJSON(JSOBJ _obj, JSONTypeContext *tc, npy_datetimestruct dts; PyDatetimeScalarObject *obj = (PyDatetimeScalarObject *)_obj; PRINTMARK(); + // TODO(anyone): Does not appear to be reached in tests. - pandas_datetime_to_datetimestruct( - obj->obval, (NPY_DATETIMEUNIT)obj->obmeta.base, &dts); + pandas_datetime_to_datetimestruct(obj->obval, + (NPY_DATETIMEUNIT)obj->obmeta.base, &dts); return PandasDateTimeStructToJSON(&dts, tc, outValue, _outLen); } static void *PyDateTimeToJSON(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen) { npy_datetimestruct dts; - PyObject *obj = (PyObject *)_obj; + PyDateTime_Date *obj = (PyDateTime_Date *)_obj; PRINTMARK(); diff --git a/pandas/_libs/tslibs/np_datetime.pyx b/pandas/_libs/tslibs/np_datetime.pyx index 3c0fe98ee7b7d..76838c7a23b24 100644 --- a/pandas/_libs/tslibs/np_datetime.pyx +++ b/pandas/_libs/tslibs/np_datetime.pyx @@ -147,6 +147,9 @@ cdef inline void td64_to_tdstruct(int64_t td64, cdef inline int64_t pydatetime_to_dt64(datetime val, npy_datetimestruct *dts): + """ + Note we are assuming that the datetime object is timezone-naive. + """ dts.year = PyDateTime_GET_YEAR(val) dts.month = PyDateTime_GET_MONTH(val) dts.day = PyDateTime_GET_DAY(val) @@ -158,8 +161,7 @@ cdef inline int64_t pydatetime_to_dt64(datetime val, return dtstruct_to_dt64(dts) -cdef inline int64_t pydate_to_dt64(date val, - npy_datetimestruct *dts): +cdef inline int64_t pydate_to_dt64(date val, npy_datetimestruct *dts): dts.year = PyDateTime_GET_YEAR(val) dts.month = PyDateTime_GET_MONTH(val) dts.day = PyDateTime_GET_DAY(val)