@@ -7,6 +7,7 @@ from cython import Py_ssize_t
7
7
from cpython.datetime cimport (
8
8
PyDelta_Check,
9
9
datetime,
10
+ datetime_new,
10
11
import_datetime,
11
12
timedelta,
12
13
tzinfo,
@@ -43,6 +44,7 @@ from pandas._libs.tslibs.timezones cimport (
43
44
is_fixed_offset,
44
45
is_tzlocal,
45
46
is_utc,
47
+ utc_pytz,
46
48
)
47
49
48
50
@@ -61,7 +63,7 @@ cdef int64_t tz_localize_to_utc_single(
61
63
return val
62
64
63
65
elif is_tzlocal(tz):
64
- return _tz_convert_tzlocal_utc (val, tz, to_utc = True )
66
+ return val - _tz_localize_using_tzinfo_api (val, tz, to_utc = True )
65
67
66
68
elif is_fixed_offset(tz):
67
69
# TODO: in this case we should be able to use get_utcoffset,
@@ -142,7 +144,7 @@ timedelta-like}
142
144
if v == NPY_NAT:
143
145
result[i] = NPY_NAT
144
146
else :
145
- result[i] = _tz_convert_tzlocal_utc (v, tz, to_utc = True )
147
+ result[i] = v - _tz_localize_using_tzinfo_api (v, tz, to_utc = True )
146
148
return result
147
149
148
150
# silence false-positive compiler warning
@@ -402,7 +404,7 @@ cdef ndarray[int64_t] _get_dst_hours(
402
404
# ----------------------------------------------------------------------
403
405
# Timezone Conversion
404
406
405
- cdef int64_t tz_convert_utc_to_tzlocal (
407
+ cdef int64_t localize_tzinfo_api (
406
408
int64_t utc_val, tzinfo tz, bint* fold = NULL
407
409
) except ? - 1 :
408
410
"""
@@ -416,20 +418,21 @@ cdef int64_t tz_convert_utc_to_tzlocal(
416
418
417
419
Returns
418
420
-------
419
- local_val : int64_t
421
+ delta : int64_t
422
+ Value to add when converting from utc.
420
423
"""
421
- return _tz_convert_tzlocal_utc (utc_val, tz, to_utc = False , fold = fold)
424
+ return _tz_localize_using_tzinfo_api (utc_val, tz, to_utc = False , fold = fold)
422
425
423
426
424
- cpdef int64_t tz_convert_from_utc_single(int64_t val , tzinfo tz):
427
+ cpdef int64_t tz_convert_from_utc_single(int64_t utc_val , tzinfo tz):
425
428
"""
426
429
Convert the val (in i8) from UTC to tz
427
430
428
431
This is a single value version of tz_convert_from_utc.
429
432
430
433
Parameters
431
434
----------
432
- val : int64
435
+ utc_val : int64
433
436
tz : tzinfo
434
437
435
438
Returns
@@ -443,22 +446,22 @@ cpdef int64_t tz_convert_from_utc_single(int64_t val, tzinfo tz):
443
446
int64_t* tdata
444
447
intp_t pos
445
448
446
- if val == NPY_NAT:
447
- return val
449
+ if utc_val == NPY_NAT:
450
+ return utc_val
448
451
449
452
if is_utc(tz):
450
- return val
453
+ return utc_val
451
454
elif is_tzlocal(tz):
452
- return _tz_convert_tzlocal_utc(val , tz, to_utc = False )
455
+ return utc_val + _tz_localize_using_tzinfo_api(utc_val , tz, to_utc = False )
453
456
elif is_fixed_offset(tz):
454
457
_, deltas, _ = get_dst_info(tz)
455
458
delta = deltas[0 ]
456
- return val + delta
459
+ return utc_val + delta
457
460
else :
458
461
trans, deltas, _ = get_dst_info(tz)
459
462
tdata = < int64_t* > cnp.PyArray_DATA(trans)
460
- pos = bisect_right_i8(tdata, val , trans.shape[0 ]) - 1
461
- return val + deltas[pos]
463
+ pos = bisect_right_i8(tdata, utc_val , trans.shape[0 ]) - 1
464
+ return utc_val + deltas[pos]
462
465
463
466
464
467
def tz_convert_from_utc (const int64_t[:] vals , tzinfo tz ):
@@ -486,32 +489,34 @@ def tz_convert_from_utc(const int64_t[:] vals, tzinfo tz):
486
489
487
490
@ cython.boundscheck (False )
488
491
@ cython.wraparound (False )
489
- cdef const int64_t[:] _tz_convert_from_utc(const int64_t[:] vals , tzinfo tz):
492
+ cdef const int64_t[:] _tz_convert_from_utc(const int64_t[:] stamps , tzinfo tz):
490
493
"""
491
494
Convert the given values (in i8) either to UTC or from UTC.
492
495
493
496
Parameters
494
497
----------
495
- vals : int64 ndarray
498
+ stamps : int64 ndarray
496
499
tz : tzinfo
497
500
498
501
Returns
499
502
-------
500
503
converted : ndarray[int64_t]
501
504
"""
502
505
cdef:
503
- int64_t[::1 ] converted, deltas
504
- Py_ssize_t i, ntrans = - 1 , n = vals.shape[0 ]
505
- int64_t val, delta = 0 # avoid not-initialized-warning
506
- intp_t pos
506
+ Py_ssize_t i, ntrans = - 1 , n = stamps.shape[0 ]
507
507
ndarray[int64_t] trans
508
+ int64_t[::1 ] deltas
508
509
int64_t* tdata = NULL
510
+ intp_t pos
511
+ int64_t utc_val, local_val, delta = NPY_NAT
512
+ bint use_utc = False , use_tzlocal = False , use_fixed = False
509
513
str typ
510
- bint use_tzlocal = False , use_fixed = False , use_utc = True
514
+
515
+ int64_t[::1 ] result
511
516
512
517
if is_utc(tz):
513
518
# Much faster than going through the "standard" pattern below
514
- return vals .copy()
519
+ return stamps .copy()
515
520
516
521
if is_utc(tz) or tz is None :
517
522
use_utc = True
@@ -520,59 +525,62 @@ cdef const int64_t[:] _tz_convert_from_utc(const int64_t[:] vals, tzinfo tz):
520
525
else :
521
526
trans, deltas, typ = get_dst_info(tz)
522
527
ntrans = trans.shape[0 ]
523
-
524
528
if typ not in [" pytz" , " dateutil" ]:
525
- # FixedOffset, we know len(deltas) == 1
526
- delta = deltas[0 ]
529
+ # static/fixed; in this case we know that len(delta) == 1
527
530
use_fixed = True
531
+ delta = deltas[0 ]
528
532
else :
529
533
tdata = < int64_t* > cnp.PyArray_DATA(trans)
530
534
531
- converted = np.empty(n, dtype = np.int64)
535
+ result = np.empty(n, dtype = np.int64)
532
536
533
537
for i in range (n):
534
- val = vals [i]
535
- if val == NPY_NAT:
536
- converted [i] = NPY_NAT
538
+ utc_val = stamps [i]
539
+ if utc_val == NPY_NAT:
540
+ result [i] = NPY_NAT
537
541
continue
538
542
539
543
# The pattern used in vectorized.pyx checks for use_utc here,
540
544
# but we handle that case above.
541
545
if use_tzlocal:
542
- converted[i] = _tz_convert_tzlocal_utc(val , tz, to_utc = False )
546
+ local_val = utc_val + _tz_localize_using_tzinfo_api(utc_val , tz, to_utc = False )
543
547
elif use_fixed:
544
- converted[i] = val + delta
548
+ local_val = utc_val + delta
545
549
else :
546
- pos = bisect_right_i8(tdata, val, ntrans) - 1
547
- converted[i] = val + deltas[pos]
550
+ pos = bisect_right_i8(tdata, utc_val, ntrans) - 1
551
+ local_val = utc_val + deltas[pos]
552
+
553
+ result[i] = local_val
548
554
549
- return converted
555
+ return result
550
556
551
557
552
558
# OSError may be thrown by tzlocal on windows at or close to 1970-01-01
553
559
# see https://github.com/pandas-dev/pandas/pull/37591#issuecomment-720628241
554
- cdef int64_t _tz_convert_tzlocal_utc(int64_t val, tzinfo tz, bint to_utc = True ,
555
- bint* fold = NULL ) except ? - 1 :
560
+ cdef int64_t _tz_localize_using_tzinfo_api(
561
+ int64_t val, tzinfo tz, bint to_utc = True , bint* fold = NULL
562
+ ) except ? - 1 :
556
563
"""
557
- Convert the i8 representation of a datetime from a tzlocal timezone to
558
- UTC, or vice-versa.
564
+ Convert the i8 representation of a datetime from a general-cast timezone to
565
+ UTC, or vice-versa using the datetime/tzinfo API .
559
566
560
- Private, not intended for use outside of tslibs.conversion
567
+ Private, not intended for use outside of tslibs.tzconversion.
561
568
562
569
Parameters
563
570
----------
564
571
val : int64_t
565
572
tz : tzinfo
566
573
to_utc : bint
567
- True if converting tzlocal _to_ UTC, False if going the other direction
574
+ True if converting _to_ UTC, False if going the other direction.
568
575
fold : bint*, default NULL
569
576
pointer to fold: whether datetime ends up in a fold or not
570
- after adjustment
577
+ after adjustment.
571
578
Only passed with to_utc=False.
572
579
573
580
Returns
574
581
-------
575
- result : int64_t
582
+ delta : int64_t
583
+ Value to add when converting from utc, subtract when converting to utc.
576
584
577
585
Notes
578
586
-----
@@ -586,23 +594,21 @@ cdef int64_t _tz_convert_tzlocal_utc(int64_t val, tzinfo tz, bint to_utc=True,
586
594
587
595
dt64_to_dtstruct(val, & dts)
588
596
589
- dt = datetime(dts.year, dts.month, dts.day, dts.hour,
590
- dts.min, dts.sec, dts.us)
591
-
592
- # tz.utcoffset only makes sense if datetime
593
- # is _wall time_, so if val is a UTC timestamp convert to wall time
597
+ # datetime_new is cython-optimized constructor
594
598
if not to_utc:
595
- dt = dt.replace(tzinfo = tzutc())
599
+ # tz.utcoffset only makes sense if datetime
600
+ # is _wall time_, so if val is a UTC timestamp convert to wall time
601
+ dt = datetime_new(dts.year, dts.month, dts.day, dts.hour,
602
+ dts.min, dts.sec, dts.us, utc_pytz)
596
603
dt = dt.astimezone(tz)
597
604
598
605
if fold is not NULL :
599
606
# NB: fold is only passed with to_utc=False
600
607
fold[0 ] = dt.fold
608
+ else :
609
+ dt = datetime_new(dts.year, dts.month, dts.day, dts.hour,
610
+ dts.min, dts.sec, dts.us, None )
601
611
602
612
td = tz.utcoffset(dt)
603
613
delta = int (td.total_seconds() * 1 _000_000_000)
604
-
605
- if to_utc:
606
- return val - delta
607
- else :
608
- return val + delta
614
+ return delta
0 commit comments