Skip to content

REF: implement utc_val_to_local_val #46847

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 9 commits into from
Apr 27, 2022
Merged
90 changes: 32 additions & 58 deletions pandas/_libs/tslibs/vectorized.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,31 @@ cdef class Localizer:
else:
trans, deltas, typ = get_dst_info(tz)
self.trans = trans
self.ntrans = trans.shape[0]
self.ntrans = self.trans.shape[0]
self.deltas = deltas

if typ != "pytz" and typ != "dateutil":
# static/fixed; in this case we know that len(delta) == 1
self.use_fixed = True
self.delta = deltas[0]
self.delta = self.deltas[0]
else:
self.use_dst = True
if typ == "pytz":
self.use_pytz = True

self.tdata = <int64_t*>cnp.PyArray_DATA(self.trans)

@cython.boundscheck(False)
cdef inline int64_t utc_val_to_local_val(self, int64_t utc_val, Py_ssize_t* pos) except? -1:
if self.use_utc:
return utc_val
elif self.use_tzlocal:
return utc_val + localize_tzinfo_api(utc_val, self.tz)
elif self.use_fixed:
return utc_val + self.delta
else:
pos[0] = bisect_right_i8(self.tdata, utc_val, self.ntrans) - 1
return utc_val + self.deltas[pos[0]]


@cython.boundscheck(False)
@cython.wraparound(False)
Expand Down Expand Up @@ -186,7 +197,8 @@ def ints_to_pydatetime(
cdef:
Localizer info = Localizer(tz)
int64_t utc_val, local_val
Py_ssize_t pos, i, n = stamps.shape[0]
Py_ssize_t i, n = stamps.shape[0]
Py_ssize_t pos = -1 # unused, avoid not-initialized warning

npy_datetimestruct dts
tzinfo new_tz
Expand Down Expand Up @@ -215,19 +227,10 @@ def ints_to_pydatetime(
result[i] = <object>NaT
continue

if info.use_utc:
local_val = utc_val
elif info.use_tzlocal:
local_val = utc_val + localize_tzinfo_api(utc_val, tz)
elif info.use_fixed:
local_val = utc_val + info.delta
else:
pos = bisect_right_i8(info.tdata, utc_val, info.ntrans) - 1
local_val = utc_val + info.deltas[pos]

if info.use_pytz:
# find right representation of dst etc in pytz timezone
new_tz = tz._tzinfos[tz._transition_info[pos]]
local_val = info.utc_val_to_local_val(utc_val, &pos)
if info.use_pytz:
# find right representation of dst etc in pytz timezone
new_tz = tz._tzinfos[tz._transition_info[pos]]

dt64_to_dtstruct(local_val, &dts)

Expand Down Expand Up @@ -269,7 +272,8 @@ def get_resolution(const int64_t[:] stamps, tzinfo tz=None) -> Resolution:
cdef:
Localizer info = Localizer(tz)
int64_t utc_val, local_val
Py_ssize_t pos, i, n = stamps.shape[0]
Py_ssize_t i, n = stamps.shape[0]
Py_ssize_t pos = -1 # unused, avoid not-initialized warning

npy_datetimestruct dts
c_Resolution reso = c_Resolution.RESO_DAY, curr_reso
Expand All @@ -279,15 +283,7 @@ def get_resolution(const int64_t[:] stamps, tzinfo tz=None) -> Resolution:
if utc_val == NPY_NAT:
continue

if info.use_utc:
local_val = utc_val
elif info.use_tzlocal:
local_val = utc_val + localize_tzinfo_api(utc_val, tz)
elif info.use_fixed:
local_val = utc_val + info.delta
else:
pos = bisect_right_i8(info.tdata, utc_val, info.ntrans) - 1
local_val = utc_val + info.deltas[pos]
local_val = info.utc_val_to_local_val(utc_val, &pos)

dt64_to_dtstruct(local_val, &dts)
curr_reso = _reso_stamp(&dts)
Expand Down Expand Up @@ -321,7 +317,8 @@ cpdef ndarray[int64_t] normalize_i8_timestamps(const int64_t[:] stamps, tzinfo t
cdef:
Localizer info = Localizer(tz)
int64_t utc_val, local_val
Py_ssize_t pos, i, n = stamps.shape[0]
Py_ssize_t i, n = stamps.shape[0]
Py_ssize_t pos = -1 # unused, avoid not-initialized warning

int64_t[::1] result = np.empty(n, dtype=np.int64)

Expand All @@ -331,15 +328,7 @@ cpdef ndarray[int64_t] normalize_i8_timestamps(const int64_t[:] stamps, tzinfo t
result[i] = NPY_NAT
continue

if info.use_utc:
local_val = utc_val
elif info.use_tzlocal:
local_val = utc_val + localize_tzinfo_api(utc_val, tz)
elif info.use_fixed:
local_val = utc_val + info.delta
else:
pos = bisect_right_i8(info.tdata, utc_val, info.ntrans) - 1
local_val = utc_val + info.deltas[pos]
local_val = info.utc_val_to_local_val(utc_val, &pos)

result[i] = local_val - (local_val % DAY_NANOS)

Expand All @@ -366,19 +355,12 @@ def is_date_array_normalized(const int64_t[:] stamps, tzinfo tz=None) -> bool:
cdef:
Localizer info = Localizer(tz)
int64_t utc_val, local_val
Py_ssize_t pos, i, n = stamps.shape[0]
Py_ssize_t i, n = stamps.shape[0]
Py_ssize_t pos = -1 # unused, avoid not-initialized warning

for i in range(n):
utc_val = stamps[i]
if info.use_utc:
local_val = utc_val
elif info.use_tzlocal:
local_val = utc_val + localize_tzinfo_api(utc_val, tz)
elif info.use_fixed:
local_val = utc_val + info.delta
else:
pos = bisect_right_i8(info.tdata, utc_val, info.ntrans) - 1
local_val = utc_val + info.deltas[pos]
local_val = info.utc_val_to_local_val(utc_val, &pos)

if local_val % DAY_NANOS != 0:
return False
Expand All @@ -395,7 +377,8 @@ def dt64arr_to_periodarr(ndarray stamps, int freq, tzinfo tz):
# stamps is int64_t, arbitrary ndim
cdef:
Localizer info = Localizer(tz)
Py_ssize_t pos, i, n = stamps.size
Py_ssize_t i, n = stamps.size
Py_ssize_t pos = -1 # unused, avoid not-initialized warning
int64_t utc_val, local_val, res_val

npy_datetimestruct dts
Expand All @@ -409,16 +392,7 @@ def dt64arr_to_periodarr(ndarray stamps, int freq, tzinfo tz):
if utc_val == NPY_NAT:
res_val = NPY_NAT
else:
if info.use_utc:
local_val = utc_val
elif info.use_tzlocal:
local_val = utc_val + localize_tzinfo_api(utc_val, tz)
elif info.use_fixed:
local_val = utc_val + info.delta
else:
pos = bisect_right_i8(info.tdata, utc_val, info.ntrans) - 1
local_val = utc_val + info.deltas[pos]

local_val = info.utc_val_to_local_val(utc_val, &pos)
dt64_to_dtstruct(local_val, &dts)
res_val = get_period_ordinal(&dts, freq)

Expand Down