Skip to content

Commit 60ac973

Browse files
authored
CLN: tzconversion (#46926)
1 parent d37643a commit 60ac973

File tree

2 files changed

+67
-64
lines changed

2 files changed

+67
-64
lines changed

pandas/_libs/tslibs/tzconversion.pxd

-12
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,13 @@ from numpy cimport (
66
)
77

88

9-
cdef int64_t localize_tzinfo_api(
10-
int64_t utc_val, tzinfo tz, bint* fold=*
11-
) except? -1
129
cdef int64_t tz_convert_from_utc_single(
1310
int64_t utc_val, tzinfo tz, bint* fold=?, Py_ssize_t* outpos=?
1411
) except? -1
1512
cdef int64_t tz_localize_to_utc_single(
1613
int64_t val, tzinfo tz, object ambiguous=*, object nonexistent=*
1714
) except? -1
1815

19-
cdef Py_ssize_t bisect_right_i8(int64_t *data, int64_t val, Py_ssize_t n)
20-
21-
cdef bint infer_dateutil_fold(
22-
int64_t value,
23-
const int64_t[::1] trans,
24-
const int64_t[::1] deltas,
25-
Py_ssize_t pos,
26-
)
27-
2816

2917
cdef class Localizer:
3018
cdef:

pandas/_libs/tslibs/tzconversion.pyx

+67-52
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ cdef const int64_t[::1] _deltas_placeholder = np.array([], dtype=np.int64)
5050

5151

5252
@cython.freelist(16)
53-
#@cython.internal
5453
@cython.final
5554
cdef class Localizer:
5655
# cdef:
@@ -102,13 +101,15 @@ cdef class Localizer:
102101
if self.use_utc:
103102
return utc_val
104103
elif self.use_tzlocal:
105-
return utc_val + localize_tzinfo_api(utc_val, self.tz, fold)
104+
return utc_val + _tz_localize_using_tzinfo_api(
105+
utc_val, self.tz, to_utc=False, fold=fold
106+
)
106107
elif self.use_fixed:
107108
return utc_val + self.delta
108109
else:
109110
pos[0] = bisect_right_i8(self.tdata, utc_val, self.ntrans) - 1
110111
if fold is not NULL:
111-
fold[0] = infer_dateutil_fold(
112+
fold[0] = _infer_dateutil_fold(
112113
utc_val, self.trans, self.deltas, pos[0]
113114
)
114115

@@ -184,10 +185,10 @@ timedelta-like}
184185
cdef:
185186
const int64_t[::1] deltas
186187
ndarray[uint8_t, cast=True] ambiguous_array
187-
Py_ssize_t i, isl, isr, idx, pos, ntrans, n = vals.shape[0]
188+
Py_ssize_t i, idx, pos, ntrans, n = vals.shape[0]
188189
Py_ssize_t delta_idx_offset, delta_idx, pos_left, pos_right
189190
int64_t *tdata
190-
int64_t v, left, right, val, v_left, v_right, new_local, remaining_mins
191+
int64_t v, left, right, val, new_local, remaining_mins
191192
int64_t first_delta, delta
192193
int64_t shift_delta = 0
193194
ndarray[int64_t] trans, result_a, result_b, dst_hours
@@ -202,7 +203,7 @@ timedelta-like}
202203
if is_utc(tz) or tz is None:
203204
return vals.copy()
204205

205-
result = np.empty(n, dtype=np.int64)
206+
result = cnp.PyArray_EMPTY(vals.ndim, vals.shape, cnp.NPY_INT64, 0)
206207

207208
if is_tzlocal(tz) or is_zoneinfo(tz):
208209
for i in range(n):
@@ -265,40 +266,7 @@ timedelta-like}
265266

266267
# Determine whether each date lies left of the DST transition (store in
267268
# result_a) or right of the DST transition (store in result_b)
268-
result_a = np.empty(n, dtype=np.int64)
269-
result_b = np.empty(n, dtype=np.int64)
270-
271-
for i in range(n):
272-
# This loops resembles the "Find the two best possibilities" block
273-
# in pytz's DstTZInfo.localize method.
274-
result_a[i] = NPY_NAT
275-
result_b[i] = NPY_NAT
276-
277-
val = vals[i]
278-
if val == NPY_NAT:
279-
continue
280-
281-
# TODO: be careful of overflow in val-DAY_NANOS
282-
isl = bisect_right_i8(tdata, val - DAY_NANOS, ntrans) - 1
283-
if isl < 0:
284-
isl = 0
285-
286-
v_left = val - deltas[isl]
287-
pos_left = bisect_right_i8(tdata, v_left, ntrans) - 1
288-
# timestamp falls to the left side of the DST transition
289-
if v_left + deltas[pos_left] == val:
290-
result_a[i] = v_left
291-
292-
# TODO: be careful of overflow in val+DAY_NANOS
293-
isr = bisect_right_i8(tdata, val + DAY_NANOS, ntrans) - 1
294-
if isr < 0:
295-
isr = 0
296-
297-
v_right = val - deltas[isr]
298-
pos_right = bisect_right_i8(tdata, v_right, ntrans) - 1
299-
# timestamp falls to the right side of the DST transition
300-
if v_right + deltas[pos_right] == val:
301-
result_b[i] = v_right
269+
result_a, result_b =_get_utc_bounds(vals, tdata, ntrans, deltas)
302270

303271
# silence false-positive compiler warning
304272
dst_hours = np.empty(0, dtype=np.int64)
@@ -417,6 +385,59 @@ cdef inline str _render_tstamp(int64_t val):
417385
return str(Timestamp(val))
418386

419387

388+
cdef _get_utc_bounds(
389+
ndarray vals,
390+
int64_t* tdata,
391+
Py_ssize_t ntrans,
392+
const int64_t[::1] deltas,
393+
):
394+
# Determine whether each date lies left of the DST transition (store in
395+
# result_a) or right of the DST transition (store in result_b)
396+
397+
cdef:
398+
ndarray result_a, result_b
399+
Py_ssize_t i, n = vals.size
400+
int64_t val, v_left, v_right
401+
Py_ssize_t isl, isr, pos_left, pos_right
402+
403+
result_a = cnp.PyArray_EMPTY(vals.ndim, vals.shape, cnp.NPY_INT64, 0)
404+
result_b = cnp.PyArray_EMPTY(vals.ndim, vals.shape, cnp.NPY_INT64, 0)
405+
406+
for i in range(n):
407+
# This loops resembles the "Find the two best possibilities" block
408+
# in pytz's DstTZInfo.localize method.
409+
result_a[i] = NPY_NAT
410+
result_b[i] = NPY_NAT
411+
412+
val = vals[i]
413+
if val == NPY_NAT:
414+
continue
415+
416+
# TODO: be careful of overflow in val-DAY_NANOS
417+
isl = bisect_right_i8(tdata, val - DAY_NANOS, ntrans) - 1
418+
if isl < 0:
419+
isl = 0
420+
421+
v_left = val - deltas[isl]
422+
pos_left = bisect_right_i8(tdata, v_left, ntrans) - 1
423+
# timestamp falls to the left side of the DST transition
424+
if v_left + deltas[pos_left] == val:
425+
result_a[i] = v_left
426+
427+
# TODO: be careful of overflow in val+DAY_NANOS
428+
isr = bisect_right_i8(tdata, val + DAY_NANOS, ntrans) - 1
429+
if isr < 0:
430+
isr = 0
431+
432+
v_right = val - deltas[isr]
433+
pos_right = bisect_right_i8(tdata, v_right, ntrans) - 1
434+
# timestamp falls to the right side of the DST transition
435+
if v_right + deltas[pos_right] == val:
436+
result_b[i] = v_right
437+
438+
return result_a, result_b
439+
440+
420441
@cython.boundscheck(False)
421442
cdef ndarray[int64_t] _get_dst_hours(
422443
# vals only needed here to potential render an exception message
@@ -433,10 +454,10 @@ cdef ndarray[int64_t] _get_dst_hours(
433454
intp_t switch_idx
434455
int64_t left, right
435456

436-
dst_hours = np.empty(n, dtype=np.int64)
457+
dst_hours = cnp.PyArray_EMPTY(result_a.ndim, result_a.shape, cnp.NPY_INT64, 0)
437458
dst_hours[:] = NPY_NAT
438459

439-
mismatch = np.zeros(n, dtype=bool)
460+
mismatch = cnp.PyArray_ZEROS(result_a.ndim, result_a.shape, cnp.NPY_BOOL, 0)
440461

441462
for i in range(n):
442463
left = result_a[i]
@@ -450,6 +471,7 @@ cdef ndarray[int64_t] _get_dst_hours(
450471
trans_idx = mismatch.nonzero()[0]
451472

452473
if trans_idx.size == 1:
474+
# TODO: not reached in tests 2022-05-02; possible?
453475
stamp = _render_tstamp(vals[trans_idx[0]])
454476
raise pytz.AmbiguousTimeError(
455477
f"Cannot infer dst time from {stamp} as there "
@@ -471,13 +493,15 @@ cdef ndarray[int64_t] _get_dst_hours(
471493

472494
delta = np.diff(result_a[grp])
473495
if grp.size == 1 or np.all(delta > 0):
496+
# TODO: not reached in tests 2022-05-02; possible?
474497
stamp = _render_tstamp(vals[grp[0]])
475498
raise pytz.AmbiguousTimeError(stamp)
476499

477500
# Find the index for the switch and pull from a for dst and b
478501
# for standard
479502
switch_idxs = (delta <= 0).nonzero()[0]
480503
if switch_idxs.size > 1:
504+
# TODO: not reached in tests 2022-05-02; possible?
481505
raise pytz.AmbiguousTimeError(
482506
f"There are {switch_idxs.size} dst switches when "
483507
"there should only be 1."
@@ -495,15 +519,6 @@ cdef ndarray[int64_t] _get_dst_hours(
495519
# ----------------------------------------------------------------------
496520
# Timezone Conversion
497521

498-
cdef int64_t localize_tzinfo_api(
499-
int64_t utc_val, tzinfo tz, bint* fold=NULL
500-
) except? -1:
501-
"""
502-
See _tz_localize_using_tzinfo_api.__doc__
503-
"""
504-
return _tz_localize_using_tzinfo_api(utc_val, tz, to_utc=False, fold=fold)
505-
506-
507522
def py_tz_convert_from_utc_single(int64_t utc_val, tzinfo tz):
508523
# The 'bint* fold=NULL' in tz_convert_from_utc_single means we cannot
509524
# make it cdef, so this is version exposed for testing from python.
@@ -608,7 +623,7 @@ cdef int64_t _tz_localize_using_tzinfo_api(
608623
# NB: relies on dateutil internals, subject to change.
609624
@cython.boundscheck(False)
610625
@cython.wraparound(False)
611-
cdef bint infer_dateutil_fold(
626+
cdef bint _infer_dateutil_fold(
612627
int64_t value,
613628
const int64_t[::1] trans,
614629
const int64_t[::1] deltas,

0 commit comments

Comments
 (0)