diff --git a/pandas/_libs/algos_common_helper.pxi.in b/pandas/_libs/algos_common_helper.pxi.in index c6338216eb7a2..991566f9b7143 100644 --- a/pandas/_libs/algos_common_helper.pxi.in +++ b/pandas/_libs/algos_common_helper.pxi.in @@ -65,7 +65,8 @@ def ensure_{{name}}(object arr, copy=True): if (arr).descr.type_num == NPY_{{c_type}}: return arr else: - return arr.astype(np.{{dtype}}, copy=copy) + # equiv: arr.astype(np.{{dtype}}, copy=copy) + return cnp.PyArray_Cast(arr, cnp.NPY_{{c_type}}) else: return np.array(arr, dtype=np.{{dtype}}) diff --git a/pandas/_libs/tslib.pyx b/pandas/_libs/tslib.pyx index 5c0b69d51367a..93622e0cd1e66 100644 --- a/pandas/_libs/tslib.pyx +++ b/pandas/_libs/tslib.pyx @@ -424,7 +424,7 @@ cpdef array_to_datetime( """ cdef: Py_ssize_t i, n = len(values) - object val, py_dt, tz, tz_out = None + object val, tz ndarray[int64_t] iresult ndarray[object] oresult npy_datetimestruct dts @@ -443,6 +443,8 @@ cpdef array_to_datetime( float offset_seconds, tz_offset set out_tzoffset_vals = set() bint string_to_dts_failed + datetime py_dt + tzinfo tz_out = None # specify error conditions assert is_raise or is_ignore or is_coerce @@ -647,6 +649,8 @@ cpdef array_to_datetime( return result, tz_out +@cython.wraparound(False) +@cython.boundscheck(False) cdef ndarray[object] ignore_errors_out_of_bounds_fallback(ndarray[object] values): """ Fallback for array_to_datetime if an OutOfBoundsDatetime is raised diff --git a/pandas/_libs/tslibs/conversion.pyx b/pandas/_libs/tslibs/conversion.pyx index 280a2b23f51af..ac8dd1d536627 100644 --- a/pandas/_libs/tslibs/conversion.pyx +++ b/pandas/_libs/tslibs/conversion.pyx @@ -581,55 +581,62 @@ cdef _TSObject _convert_str_to_tsobject(object ts, tzinfo tz, str unit, """ cdef: npy_datetimestruct dts - int out_local = 0, out_tzoffset = 0 - bint do_parse_datetime_string = False + int out_local = 0, out_tzoffset = 0, string_to_dts_failed + datetime dt + int64_t ival if len(ts) == 0 or ts in nat_strings: ts = NaT + obj = _TSObject() + obj.value = NPY_NAT + obj.tzinfo = tz + return obj elif ts == 'now': # Issue 9000, we short-circuit rather than going # into np_datetime_strings which returns utc - ts = datetime.now(tz) + dt = datetime.now(tz) elif ts == 'today': # Issue 9000, we short-circuit rather than going # into np_datetime_strings which returns a normalized datetime - ts = datetime.now(tz) + dt = datetime.now(tz) # equiv: datetime.today().replace(tzinfo=tz) else: string_to_dts_failed = _string_to_dts( ts, &dts, &out_local, &out_tzoffset, False ) - try: - if not string_to_dts_failed: + if not string_to_dts_failed: + try: check_dts_bounds(&dts) if out_local == 1: return _create_tsobject_tz_using_offset(dts, out_tzoffset, tz) else: - ts = dtstruct_to_dt64(&dts) + ival = dtstruct_to_dt64(&dts) if tz is not None: # shift for _localize_tso - ts = tz_localize_to_utc_single(ts, tz, - ambiguous="raise") + ival = tz_localize_to_utc_single(ival, tz, + ambiguous="raise") - except OutOfBoundsDatetime: - # GH#19382 for just-barely-OutOfBounds falling back to dateutil - # parser will return incorrect result because it will ignore - # nanoseconds - raise + return convert_to_tsobject(ival, tz, None, False, False) - except ValueError: - do_parse_datetime_string = True + except OutOfBoundsDatetime: + # GH#19382 for just-barely-OutOfBounds falling back to dateutil + # parser will return incorrect result because it will ignore + # nanoseconds + raise - if string_to_dts_failed or do_parse_datetime_string: - try: - ts = parse_datetime_string(ts, dayfirst=dayfirst, - yearfirst=yearfirst) - except (ValueError, OverflowError): - raise ValueError("could not convert string to Timestamp") + except ValueError: + # Fall through to parse_datetime_string + pass + + try: + dt = parse_datetime_string(ts, dayfirst=dayfirst, + yearfirst=yearfirst) + except (ValueError, OverflowError): + raise ValueError("could not convert string to Timestamp") - return convert_to_tsobject(ts, tz, unit, dayfirst, yearfirst) + return convert_datetime_to_tsobject(dt, tz) cdef inline check_overflows(_TSObject obj): @@ -688,12 +695,8 @@ cdef inline void _localize_tso(_TSObject obj, tzinfo tz): Sets obj.tzinfo inplace, alters obj.dts inplace. """ cdef: - ndarray[int64_t] trans - int64_t[::1] deltas int64_t local_val - int64_t* tdata - Py_ssize_t pos, ntrans, outpos = -1 - str typ + Py_ssize_t outpos = -1 assert obj.tzinfo is None diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 698e19f97c6aa..89fe1feaef3d9 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -397,6 +397,7 @@ cdef class _Timestamp(ABCTimestamp): elif is_datetime64_object(other): return type(self)(other) - self return NotImplemented + # ----------------------------------------------------------------- cdef int64_t _maybe_convert_value_to_local(self): diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index 95882b65e9aaa..667a7b6ed4ae1 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -812,6 +812,7 @@ def _local_timestamps(self) -> np.ndarray: were timezone-naive. """ if self.tz is None or timezones.is_utc(self.tz): + # Avoid the copy that would be made in tzconversion return self.asi8 return tzconversion.tz_convert_from_utc(self.asi8, self.tz)