Skip to content

PERF: ints_to_pydatetime #35113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 6, 2020
91 changes: 36 additions & 55 deletions pandas/_libs/tslib.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ PyDateTime_IMPORT


cimport numpy as cnp
from numpy cimport float64_t, int64_t, ndarray, uint8_t
from numpy cimport float64_t, int64_t, ndarray, uint8_t, intp_t
import numpy as np
cnp.import_array()

Expand Down Expand Up @@ -157,13 +157,15 @@ def ints_to_pydatetime(
Py_ssize_t i, n = len(arr)
ndarray[int64_t] trans
int64_t[:] deltas
Py_ssize_t pos
intp_t[:] pos
npy_datetimestruct dts
object dt, new_tz
str typ
int64_t value, delta, local_value
int64_t value, local_value, delta = NPY_NAT # dummy for delta
ndarray[object] result = np.empty(n, dtype=object)
object (*func_create)(int64_t, npy_datetimestruct, tzinfo, object, bint)
bint use_utc = False, use_tzlocal = False, use_fixed = False
bint use_pytz = False

if box == "date":
assert (tz is None), "tz should be None when converting to date"
Expand All @@ -184,66 +186,45 @@ def ints_to_pydatetime(
)

if is_utc(tz) or tz is None:
for i in range(n):
value = arr[i]
if value == NPY_NAT:
result[i] = <object>NaT
else:
dt64_to_dtstruct(value, &dts)
result[i] = func_create(value, dts, tz, freq, fold)
use_utc = True
elif is_tzlocal(tz):
for i in range(n):
value = arr[i]
if value == NPY_NAT:
result[i] = <object>NaT
else:
# Python datetime objects do not support nanosecond
# resolution (yet, PEP 564). Need to compute new value
# using the i8 representation.
local_value = tz_convert_utc_to_tzlocal(value, tz)
dt64_to_dtstruct(local_value, &dts)
result[i] = func_create(value, dts, tz, freq, fold)
use_tzlocal = True
else:
trans, deltas, typ = get_dst_info(tz)

if typ not in ['pytz', 'dateutil']:
if typ not in ["pytz", "dateutil"]:
# static/fixed; in this case we know that len(delta) == 1
use_fixed = True
delta = deltas[0]
for i in range(n):
value = arr[i]
if value == NPY_NAT:
result[i] = <object>NaT
else:
# Adjust datetime64 timestamp, recompute datetimestruct
dt64_to_dtstruct(value + delta, &dts)
result[i] = func_create(value, dts, tz, freq, fold)
else:
pos = trans.searchsorted(arr, side="right") - 1
use_pytz = typ == "pytz"

elif typ == 'dateutil':
# no zone-name change for dateutil tzs - dst etc
# represented in single object.
for i in range(n):
value = arr[i]
if value == NPY_NAT:
result[i] = <object>NaT
else:
# Adjust datetime64 timestamp, recompute datetimestruct
pos = trans.searchsorted(value, side='right') - 1
dt64_to_dtstruct(value + deltas[pos], &dts)
result[i] = func_create(value, dts, tz, freq, fold)
for i in range(n):
new_tz = tz
value = arr[i]

if value == NPY_NAT:
result[i] = <object>NaT
else:
# pytz
for i in range(n):
value = arr[i]
if value == NPY_NAT:
result[i] = <object>NaT
else:
# Adjust datetime64 timestamp, recompute datetimestruct
pos = trans.searchsorted(value, side='right') - 1
# find right representation of dst etc in pytz timezone
new_tz = tz._tzinfos[tz._transition_info[pos]]
if use_utc:
local_value = value
elif use_tzlocal:
local_value = tz_convert_utc_to_tzlocal(value, tz)
elif use_fixed:
local_value = value + delta
elif not use_pytz:
# i.e. dateutil
# no zone-name change for dateutil tzs - dst etc
# represented in single object.
local_value = value + deltas[pos[i]]
else:
# pytz
# find right representation of dst etc in pytz timezone
new_tz = tz._tzinfos[tz._transition_info[pos[i]]]
local_value = value + deltas[pos[i]]

dt64_to_dtstruct(value + deltas[pos], &dts)
result[i] = func_create(value, dts, new_tz, freq, fold)
dt64_to_dtstruct(local_value, &dts)
result[i] = func_create(value, dts, new_tz, freq, fold)

return result

Expand Down