Skip to content

Commit a6958fe

Browse files
authored
REF: use standard patterns in tslibs (#46259)
* REF: use standard patterns in tslibs * lint fixup * avoid not-initialized-warning
1 parent e139469 commit a6958fe

File tree

4 files changed

+77
-86
lines changed

4 files changed

+77
-86
lines changed

pandas/_libs/tslibs/conversion.pxd

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ from pandas._libs.tslibs.np_datetime cimport npy_datetimestruct
1212

1313

1414
cdef class _TSObject:
15-
cdef:
15+
cdef readonly:
1616
npy_datetimestruct dts # npy_datetimestruct
1717
int64_t value # numpy dt64
18-
object tzinfo
18+
tzinfo tzinfo
1919
bint fold
2020

2121

pandas/_libs/tslibs/conversion.pyx

+18-22
Original file line numberDiff line numberDiff line change
@@ -370,18 +370,13 @@ cdef class _TSObject:
370370
# cdef:
371371
# npy_datetimestruct dts # npy_datetimestruct
372372
# int64_t value # numpy dt64
373-
# object tzinfo
373+
# tzinfo tzinfo
374374
# bint fold
375375

376376
def __cinit__(self):
377377
# GH 25057. As per PEP 495, set fold to 0 by default
378378
self.fold = 0
379379

380-
@property
381-
def value(self):
382-
# This is needed in order for `value` to be accessible in lib.pyx
383-
return self.value
384-
385380

386381
cdef convert_to_tsobject(object ts, tzinfo tz, str unit,
387382
bint dayfirst, bint yearfirst, int32_t nanos=0):
@@ -541,7 +536,7 @@ cdef _TSObject _create_tsobject_tz_using_offset(npy_datetimestruct dts,
541536
int64_t value # numpy dt64
542537
datetime dt
543538
ndarray[int64_t] trans
544-
int64_t[:] deltas
539+
int64_t[::1] deltas
545540

546541
value = dtstruct_to_dt64(&dts)
547542
obj.dts = dts
@@ -711,7 +706,7 @@ cdef inline void _localize_tso(_TSObject obj, tzinfo tz):
711706
"""
712707
cdef:
713708
ndarray[int64_t] trans
714-
int64_t[:] deltas
709+
int64_t[::1] deltas
715710
int64_t local_val
716711
Py_ssize_t pos
717712
str typ
@@ -729,26 +724,27 @@ cdef inline void _localize_tso(_TSObject obj, tzinfo tz):
729724
# Adjust datetime64 timestamp, recompute datetimestruct
730725
trans, deltas, typ = get_dst_info(tz)
731726

732-
if is_fixed_offset(tz):
733-
# static/fixed tzinfo; in this case we know len(deltas) == 1
734-
# This can come back with `typ` of either "fixed" or None
735-
dt64_to_dtstruct(obj.value + deltas[0], &obj.dts)
736-
elif typ == 'pytz':
727+
if typ == "pytz":
737728
# i.e. treat_tz_as_pytz(tz)
738-
pos = trans.searchsorted(obj.value, side='right') - 1
729+
pos = trans.searchsorted(obj.value, side="right") - 1
730+
local_val = obj.value + deltas[pos]
731+
732+
# find right representation of dst etc in pytz timezone
739733
tz = tz._tzinfos[tz._transition_info[pos]]
740-
dt64_to_dtstruct(obj.value + deltas[pos], &obj.dts)
741-
elif typ == 'dateutil':
734+
elif typ == "dateutil":
742735
# i.e. treat_tz_as_dateutil(tz)
743-
pos = trans.searchsorted(obj.value, side='right') - 1
744-
dt64_to_dtstruct(obj.value + deltas[pos], &obj.dts)
736+
pos = trans.searchsorted(obj.value, side="right") - 1
737+
local_val = obj.value + deltas[pos]
738+
745739
# dateutil supports fold, so we infer fold from value
746740
obj.fold = _infer_tsobject_fold(obj, trans, deltas, pos)
747741
else:
748-
# Note: as of 2018-07-17 all tzinfo objects that are _not_
749-
# either pytz or dateutil have is_fixed_offset(tz) == True,
750-
# so this branch will never be reached.
751-
pass
742+
# All other cases have len(deltas) == 1. As of 2018-07-17
743+
# (and 2022-03-07), all test cases that get here have
744+
# is_fixed_offset(tz).
745+
local_val = obj.value + deltas[0]
746+
747+
dt64_to_dtstruct(local_val, &obj.dts)
752748

753749
obj.tzinfo = tz
754750

pandas/_libs/tslibs/tzconversion.pyx

+36-38
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ cdef int64_t tz_localize_to_utc_single(
5252
"""See tz_localize_to_utc.__doc__"""
5353
cdef:
5454
int64_t delta
55-
int64_t[:] deltas
55+
int64_t[::1] deltas
5656

5757
if val == NPY_NAT:
5858
return val
@@ -115,9 +115,10 @@ timedelta-like}
115115
localized : ndarray[int64_t]
116116
"""
117117
cdef:
118-
int64_t[:] deltas, idx_shifted, idx_shifted_left, idx_shifted_right
118+
int64_t[::1] deltas
119+
int64_t[:] idx_shifted, idx_shifted_left, idx_shifted_right
119120
ndarray[uint8_t, cast=True] ambiguous_array, both_nat, both_eq
120-
Py_ssize_t i, idx, pos, ntrans, n = len(vals)
121+
Py_ssize_t i, idx, pos, ntrans, n = vals.shape[0]
121122
Py_ssize_t delta_idx_offset, delta_idx, pos_left, pos_right
122123
int64_t *tdata
123124
int64_t v, left, right, val, v_left, v_right, new_local, remaining_mins
@@ -184,7 +185,7 @@ timedelta-like}
184185
trans, deltas, _ = get_dst_info(tz)
185186

186187
tdata = <int64_t*>cnp.PyArray_DATA(trans)
187-
ntrans = len(trans)
188+
ntrans = trans.shape[0]
188189

189190
# Determine whether each date lies left of the DST transition (store in
190191
# result_a) or right of the DST transition (store in result_b)
@@ -400,7 +401,7 @@ cpdef int64_t tz_convert_from_utc_single(int64_t val, tzinfo tz):
400401
"""
401402
cdef:
402403
int64_t delta
403-
int64_t[:] deltas
404+
int64_t[::1] deltas
404405
ndarray[int64_t, ndim=1] trans
405406
intp_t pos
406407

@@ -437,11 +438,11 @@ def tz_convert_from_utc(const int64_t[:] vals, tzinfo tz):
437438
cdef:
438439
const int64_t[:] converted
439440

440-
if len(vals) == 0:
441+
if vals.shape[0] == 0:
441442
return np.array([], dtype=np.int64)
442443

443444
converted = _tz_convert_from_utc(vals, tz)
444-
return np.array(converted, dtype=np.int64)
445+
return np.asarray(converted, dtype=np.int64)
445446

446447

447448
@cython.boundscheck(False)
@@ -460,53 +461,48 @@ cdef const int64_t[:] _tz_convert_from_utc(const int64_t[:] vals, tzinfo tz):
460461
converted : ndarray[int64_t]
461462
"""
462463
cdef:
463-
int64_t[:] converted, deltas
464-
Py_ssize_t i, n = len(vals)
465-
int64_t val, delta
464+
int64_t[::1] converted, deltas
465+
Py_ssize_t i, n = vals.shape[0]
466+
int64_t val, delta = 0 # avoid not-initialized-warning
466467
intp_t[:] pos
467468
ndarray[int64_t] trans
468469
str typ
470+
bint use_tzlocal = False, use_fixed = False, use_utc = True
469471

470472
if is_utc(tz):
471-
return vals
473+
# Much faster than going through the "standard" pattern below
474+
return vals.copy()
475+
476+
if is_utc(tz) or tz is None:
477+
use_utc = True
472478
elif is_tzlocal(tz):
473-
converted = np.empty(n, dtype=np.int64)
474-
for i in range(n):
475-
val = vals[i]
476-
if val == NPY_NAT:
477-
converted[i] = NPY_NAT
478-
else:
479-
converted[i] = _tz_convert_tzlocal_utc(val, tz, to_utc=False)
479+
use_tzlocal = True
480480
else:
481-
converted = np.empty(n, dtype=np.int64)
482-
483481
trans, deltas, typ = get_dst_info(tz)
484482

485483
if typ not in ["pytz", "dateutil"]:
486484
# FixedOffset, we know len(deltas) == 1
487485
delta = deltas[0]
488-
489-
for i in range(n):
490-
val = vals[i]
491-
if val == NPY_NAT:
492-
converted[i] = val
493-
else:
494-
converted[i] = val + delta
495-
486+
use_fixed = True
496487
else:
497488
pos = trans.searchsorted(vals, side="right") - 1
498489

499-
for i in range(n):
500-
val = vals[i]
501-
if val == NPY_NAT:
502-
converted[i] = val
503-
else:
504-
if pos[i] < 0:
505-
# TODO: How is this reached? Should we be checking for
506-
# it elsewhere?
507-
raise ValueError("First time before start of DST info")
490+
converted = np.empty(n, dtype=np.int64)
508491

509-
converted[i] = val + deltas[pos[i]]
492+
for i in range(n):
493+
val = vals[i]
494+
if val == NPY_NAT:
495+
converted[i] = NPY_NAT
496+
continue
497+
498+
# The pattern used in vectorized.pyx checks for use_utc here,
499+
# but we handle that case above.
500+
if use_tzlocal:
501+
converted[i] = _tz_convert_tzlocal_utc(val, tz, to_utc=False)
502+
elif use_fixed:
503+
converted[i] = val + delta
504+
else:
505+
converted[i] = val + deltas[pos[i]]
510506

511507
return converted
512508

@@ -547,8 +543,10 @@ cdef int64_t _tz_convert_tzlocal_utc(int64_t val, tzinfo tz, bint to_utc=True,
547543
timedelta td
548544

549545
dt64_to_dtstruct(val, &dts)
546+
550547
dt = datetime(dts.year, dts.month, dts.day, dts.hour,
551548
dts.min, dts.sec, dts.us)
549+
552550
# tz.utcoffset only makes sense if datetime
553551
# is _wall time_, so if val is a UTC timestamp convert to wall time
554552
if not to_utc:

pandas/_libs/tslibs/vectorized.pyx

+21-24
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def ints_to_pydatetime(
121121
cdef:
122122
Py_ssize_t i, n = len(stamps)
123123
ndarray[int64_t] trans
124-
int64_t[:] deltas
124+
int64_t[::1] deltas
125125
intp_t[:] pos
126126
npy_datetimestruct dts
127127
object dt, new_tz
@@ -167,26 +167,23 @@ def ints_to_pydatetime(
167167

168168
if value == NPY_NAT:
169169
result[i] = <object>NaT
170+
continue
171+
172+
if use_utc:
173+
local_val = value
174+
elif use_tzlocal:
175+
local_val = tz_convert_utc_to_tzlocal(value, tz)
176+
elif use_fixed:
177+
local_val = value + delta
170178
else:
171-
if use_utc:
172-
local_val = value
173-
elif use_tzlocal:
174-
local_val = tz_convert_utc_to_tzlocal(value, tz)
175-
elif use_fixed:
176-
local_val = value + delta
177-
elif not use_pytz:
178-
# i.e. dateutil
179-
# no zone-name change for dateutil tzs - dst etc
180-
# represented in single object.
181-
local_val = value + deltas[pos[i]]
182-
else:
183-
# pytz
184-
# find right representation of dst etc in pytz timezone
185-
new_tz = tz._tzinfos[tz._transition_info[pos[i]]]
186-
local_val = value + deltas[pos[i]]
187-
188-
dt64_to_dtstruct(local_val, &dts)
189-
result[i] = func_create(value, dts, new_tz, freq, fold)
179+
local_val = value + deltas[pos[i]]
180+
181+
if use_pytz:
182+
# find right representation of dst etc in pytz timezone
183+
new_tz = tz._tzinfos[tz._transition_info[pos[i]]]
184+
185+
dt64_to_dtstruct(local_val, &dts)
186+
result[i] = func_create(value, dts, new_tz, freq, fold)
190187

191188
return result
192189

@@ -226,7 +223,7 @@ def get_resolution(const int64_t[:] stamps, tzinfo tz=None) -> Resolution:
226223
npy_datetimestruct dts
227224
int reso = RESO_DAY, curr_reso
228225
ndarray[int64_t] trans
229-
int64_t[:] deltas
226+
int64_t[::1] deltas
230227
intp_t[:] pos
231228
int64_t local_val, delta = NPY_NAT
232229
bint use_utc = False, use_tzlocal = False, use_fixed = False
@@ -288,7 +285,7 @@ cpdef ndarray[int64_t] normalize_i8_timestamps(const int64_t[:] stamps, tzinfo t
288285
Py_ssize_t i, n = len(stamps)
289286
int64_t[:] result = np.empty(n, dtype=np.int64)
290287
ndarray[int64_t] trans
291-
int64_t[:] deltas
288+
int64_t[::1] deltas
292289
str typ
293290
Py_ssize_t[:] pos
294291
int64_t local_val, delta = NPY_NAT
@@ -346,7 +343,7 @@ def is_date_array_normalized(const int64_t[:] stamps, tzinfo tz=None) -> bool:
346343
cdef:
347344
Py_ssize_t i, n = len(stamps)
348345
ndarray[int64_t] trans
349-
int64_t[:] deltas
346+
int64_t[::1] deltas
350347
intp_t[:] pos
351348
int64_t local_val, delta = NPY_NAT
352349
str typ
@@ -392,7 +389,7 @@ def dt64arr_to_periodarr(const int64_t[:] stamps, int freq, tzinfo tz):
392389
Py_ssize_t i, n = len(stamps)
393390
int64_t[:] result = np.empty(n, dtype=np.int64)
394391
ndarray[int64_t] trans
395-
int64_t[:] deltas
392+
int64_t[::1] deltas
396393
Py_ssize_t[:] pos
397394
npy_datetimestruct dts
398395
int64_t local_val, delta = NPY_NAT

0 commit comments

Comments
 (0)