@@ -50,7 +50,6 @@ cdef const int64_t[::1] _deltas_placeholder = np.array([], dtype=np.int64)
50
50
51
51
52
52
@ cython.freelist (16 )
53
- # @cython.internal
54
53
@cython.final
55
54
cdef class Localizer:
56
55
# cdef:
@@ -102,13 +101,15 @@ cdef class Localizer:
102
101
if self .use_utc:
103
102
return utc_val
104
103
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
+ )
106
107
elif self .use_fixed:
107
108
return utc_val + self .delta
108
109
else :
109
110
pos[0 ] = bisect_right_i8(self .tdata, utc_val, self .ntrans) - 1
110
111
if fold is not NULL :
111
- fold[0 ] = infer_dateutil_fold (
112
+ fold[0 ] = _infer_dateutil_fold (
112
113
utc_val, self .trans, self .deltas, pos[0 ]
113
114
)
114
115
@@ -184,10 +185,10 @@ timedelta-like}
184
185
cdef:
185
186
const int64_t[::1 ] deltas
186
187
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 ]
188
189
Py_ssize_t delta_idx_offset, delta_idx, pos_left, pos_right
189
190
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
191
192
int64_t first_delta, delta
192
193
int64_t shift_delta = 0
193
194
ndarray[int64_t] trans, result_a, result_b, dst_hours
@@ -202,7 +203,7 @@ timedelta-like}
202
203
if is_utc(tz) or tz is None :
203
204
return vals.copy()
204
205
205
- result = np.empty(n, dtype = np.int64 )
206
+ result = cnp.PyArray_EMPTY(vals.ndim, vals.shape, cnp.NPY_INT64, 0 )
206
207
207
208
if is_tzlocal(tz) or is_zoneinfo(tz):
208
209
for i in range (n):
@@ -265,40 +266,7 @@ timedelta-like}
265
266
266
267
# Determine whether each date lies left of the DST transition (store in
267
268
# 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)
302
270
303
271
# silence false-positive compiler warning
304
272
dst_hours = np.empty(0 , dtype = np.int64)
@@ -417,6 +385,59 @@ cdef inline str _render_tstamp(int64_t val):
417
385
return str (Timestamp(val))
418
386
419
387
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
+
420
441
@ cython.boundscheck (False )
421
442
cdef ndarray[int64_t] _get_dst_hours(
422
443
# vals only needed here to potential render an exception message
@@ -433,10 +454,10 @@ cdef ndarray[int64_t] _get_dst_hours(
433
454
intp_t switch_idx
434
455
int64_t left, right
435
456
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 )
437
458
dst_hours[:] = NPY_NAT
438
459
439
- mismatch = np.zeros(n, dtype = bool )
460
+ mismatch = cnp.PyArray_ZEROS(result_a.ndim, result_a.shape, cnp.NPY_BOOL, 0 )
440
461
441
462
for i in range (n):
442
463
left = result_a[i]
@@ -450,6 +471,7 @@ cdef ndarray[int64_t] _get_dst_hours(
450
471
trans_idx = mismatch.nonzero()[0 ]
451
472
452
473
if trans_idx.size == 1 :
474
+ # TODO: not reached in tests 2022-05-02; possible?
453
475
stamp = _render_tstamp(vals[trans_idx[0 ]])
454
476
raise pytz.AmbiguousTimeError(
455
477
f" Cannot infer dst time from {stamp} as there "
@@ -471,13 +493,15 @@ cdef ndarray[int64_t] _get_dst_hours(
471
493
472
494
delta = np.diff(result_a[grp])
473
495
if grp.size == 1 or np.all(delta > 0 ):
496
+ # TODO: not reached in tests 2022-05-02; possible?
474
497
stamp = _render_tstamp(vals[grp[0 ]])
475
498
raise pytz.AmbiguousTimeError(stamp)
476
499
477
500
# Find the index for the switch and pull from a for dst and b
478
501
# for standard
479
502
switch_idxs = (delta <= 0 ).nonzero()[0 ]
480
503
if switch_idxs.size > 1 :
504
+ # TODO: not reached in tests 2022-05-02; possible?
481
505
raise pytz.AmbiguousTimeError(
482
506
f" There are {switch_idxs.size} dst switches when "
483
507
" there should only be 1."
@@ -495,15 +519,6 @@ cdef ndarray[int64_t] _get_dst_hours(
495
519
# ----------------------------------------------------------------------
496
520
# Timezone Conversion
497
521
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
-
507
522
def py_tz_convert_from_utc_single (int64_t utc_val , tzinfo tz ):
508
523
# The 'bint* fold=NULL' in tz_convert_from_utc_single means we cannot
509
524
# make it cdef, so this is version exposed for testing from python.
@@ -608,7 +623,7 @@ cdef int64_t _tz_localize_using_tzinfo_api(
608
623
# NB: relies on dateutil internals, subject to change.
609
624
@ cython.boundscheck (False )
610
625
@ cython.wraparound (False )
611
- cdef bint infer_dateutil_fold (
626
+ cdef bint _infer_dateutil_fold (
612
627
int64_t value,
613
628
const int64_t[::1 ] trans,
614
629
const int64_t[::1 ] deltas,
0 commit comments