Skip to content

Commit 5e7fabc

Browse files
jbrockmendeljreback
authored andcommitted
function for frequently repeated tzconversion code (#19625)
1 parent e2ea151 commit 5e7fabc

File tree

4 files changed

+85
-55
lines changed

4 files changed

+85
-55
lines changed

pandas/_libs/tslibs/conversion.pxd

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22
# cython: profile=False
33

4-
from cpython.datetime cimport datetime
4+
from cpython.datetime cimport datetime, tzinfo
55

66
from numpy cimport int64_t, int32_t
77

@@ -30,3 +30,5 @@ cdef int64_t get_datetime64_nanos(object val) except? -1
3030
cpdef int64_t pydt_to_i8(object pydt) except? -1
3131

3232
cdef maybe_datetimelike_to_i8(object val)
33+
34+
cdef int64_t tz_convert_utc_to_tzlocal(int64_t utc_val, tzinfo tz)

pandas/_libs/tslibs/conversion.pyx

+74-44
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ cdef inline void _localize_tso(_TSObject obj, object tz):
499499
"""
500500
cdef:
501501
ndarray[int64_t] trans, deltas
502-
int64_t delta
502+
int64_t delta, local_val
503503
Py_ssize_t posn
504504
datetime dt
505505

@@ -510,11 +510,8 @@ cdef inline void _localize_tso(_TSObject obj, object tz):
510510
elif obj.value == NPY_NAT:
511511
pass
512512
elif is_tzlocal(tz):
513-
dt64_to_dtstruct(obj.value, &obj.dts)
514-
dt = datetime(obj.dts.year, obj.dts.month, obj.dts.day, obj.dts.hour,
515-
obj.dts.min, obj.dts.sec, obj.dts.us, tz)
516-
delta = int(get_utcoffset(tz, dt).total_seconds()) * 1000000000
517-
dt64_to_dtstruct(obj.value + delta, &obj.dts)
513+
local_val = tz_convert_utc_to_tzlocal(obj.value, tz)
514+
dt64_to_dtstruct(local_val, &obj.dts)
518515
else:
519516
# Adjust datetime64 timestamp, recompute datetimestruct
520517
trans, deltas, typ = get_dst_info(tz)
@@ -556,6 +553,66 @@ cdef inline datetime _localize_pydatetime(datetime dt, tzinfo tz):
556553
# ----------------------------------------------------------------------
557554
# Timezone Conversion
558555

556+
cdef inline int64_t tz_convert_tzlocal_to_utc(int64_t val, tzinfo tz):
557+
"""
558+
Parameters
559+
----------
560+
val : int64_t
561+
tz : tzinfo
562+
563+
Returns
564+
-------
565+
utc_date : int64_t
566+
567+
See Also
568+
--------
569+
tz_convert_utc_to_tzlocal
570+
"""
571+
cdef:
572+
pandas_datetimestruct dts
573+
int64_t utc_date, delta
574+
datetime dt
575+
576+
dt64_to_dtstruct(val, &dts)
577+
dt = datetime(dts.year, dts.month, dts.day, dts.hour,
578+
dts.min, dts.sec, dts.us, tz)
579+
delta = int(get_utcoffset(tz, dt).total_seconds()) * 1000000000
580+
utc_date = val - delta
581+
return utc_date
582+
583+
584+
cdef inline int64_t tz_convert_utc_to_tzlocal(int64_t utc_val, tzinfo tz):
585+
"""
586+
Parameters
587+
----------
588+
utc_val : int64_t
589+
tz : tzinfo
590+
591+
Returns
592+
-------
593+
local_val : int64_t
594+
595+
See Also
596+
--------
597+
tz_convert_tzlocal_to_utc
598+
599+
Notes
600+
-----
601+
The key difference between this and tz_convert_tzlocal_to_utc is a
602+
an addition flipped to a subtraction in the last line.
603+
"""
604+
cdef:
605+
pandas_datetimestruct dts
606+
int64_t local_val, delta
607+
datetime dt
608+
609+
dt64_to_dtstruct(utc_val, &dts)
610+
dt = datetime(dts.year, dts.month, dts.day, dts.hour,
611+
dts.min, dts.sec, dts.us, tz)
612+
delta = int(get_utcoffset(tz, dt).total_seconds()) * 1000000000
613+
local_val = utc_val + delta
614+
return local_val
615+
559616

560617
cpdef int64_t tz_convert_single(int64_t val, object tz1, object tz2):
561618
"""
@@ -590,11 +647,7 @@ cpdef int64_t tz_convert_single(int64_t val, object tz1, object tz2):
590647

591648
# Convert to UTC
592649
if is_tzlocal(tz1):
593-
dt64_to_dtstruct(val, &dts)
594-
dt = datetime(dts.year, dts.month, dts.day, dts.hour,
595-
dts.min, dts.sec, dts.us, tz1)
596-
delta = int(get_utcoffset(tz1, dt).total_seconds()) * 1000000000
597-
utc_date = val - delta
650+
utc_date = tz_convert_tzlocal_to_utc(val, tz1)
598651
elif get_timezone(tz1) != 'UTC':
599652
trans, deltas, typ = get_dst_info(tz1)
600653
pos = trans.searchsorted(val, side='right') - 1
@@ -608,11 +661,7 @@ cpdef int64_t tz_convert_single(int64_t val, object tz1, object tz2):
608661
if get_timezone(tz2) == 'UTC':
609662
return utc_date
610663
elif is_tzlocal(tz2):
611-
dt64_to_dtstruct(utc_date, &dts)
612-
dt = datetime(dts.year, dts.month, dts.day, dts.hour,
613-
dts.min, dts.sec, dts.us, tz2)
614-
delta = int(get_utcoffset(tz2, dt).total_seconds()) * 1000000000
615-
return utc_date + delta
664+
return tz_convert_utc_to_tzlocal(utc_date, tz2)
616665

617666
# Convert UTC to other timezone
618667
trans, deltas, typ = get_dst_info(tz2)
@@ -662,12 +711,7 @@ def tz_convert(ndarray[int64_t] vals, object tz1, object tz2):
662711
if v == NPY_NAT:
663712
utc_dates[i] = NPY_NAT
664713
else:
665-
dt64_to_dtstruct(v, &dts)
666-
dt = datetime(dts.year, dts.month, dts.day, dts.hour,
667-
dts.min, dts.sec, dts.us, tz1)
668-
delta = (int(get_utcoffset(tz1, dt).total_seconds()) *
669-
1000000000)
670-
utc_dates[i] = v - delta
714+
utc_dates[i] = tz_convert_tzlocal_to_utc(v, tz1)
671715
else:
672716
trans, deltas, typ = get_dst_info(tz1)
673717

@@ -702,12 +746,7 @@ def tz_convert(ndarray[int64_t] vals, object tz1, object tz2):
702746
if v == NPY_NAT:
703747
result[i] = NPY_NAT
704748
else:
705-
dt64_to_dtstruct(v, &dts)
706-
dt = datetime(dts.year, dts.month, dts.day, dts.hour,
707-
dts.min, dts.sec, dts.us, tz2)
708-
delta = (int(get_utcoffset(tz2, dt).total_seconds()) *
709-
1000000000)
710-
result[i] = v + delta
749+
result[i] = tz_convert_utc_to_tzlocal(v, tz2)
711750
return result
712751

713752
# Convert UTC to other timezone
@@ -777,11 +816,7 @@ def tz_localize_to_utc(ndarray[int64_t] vals, object tz, object ambiguous=None,
777816
if is_tzlocal(tz):
778817
for i in range(n):
779818
v = vals[i]
780-
dt64_to_dtstruct(v, &dts)
781-
dt = datetime(dts.year, dts.month, dts.day, dts.hour,
782-
dts.min, dts.sec, dts.us, tz)
783-
delta = int(get_utcoffset(tz, dt).total_seconds()) * 1000000000
784-
result[i] = v - delta
819+
result[i] = tz_convert_tzlocal_to_utc(v, tz)
785820
return result
786821

787822
if is_string_object(ambiguous):
@@ -1024,11 +1059,8 @@ cdef ndarray[int64_t] _normalize_local(ndarray[int64_t] stamps, object tz):
10241059
if stamps[i] == NPY_NAT:
10251060
result[i] = NPY_NAT
10261061
continue
1027-
dt64_to_dtstruct(stamps[i], &dts)
1028-
dt = datetime(dts.year, dts.month, dts.day, dts.hour,
1029-
dts.min, dts.sec, dts.us, tz)
1030-
delta = int(get_utcoffset(tz, dt).total_seconds()) * 1000000000
1031-
dt64_to_dtstruct(stamps[i] + delta, &dts)
1062+
local_val = tz_convert_utc_to_tzlocal(stamps[i], tz)
1063+
dt64_to_dtstruct(local_val, &dts)
10321064
result[i] = _normalized_stamp(&dts)
10331065
else:
10341066
# Adjust datetime64 timestamp, recompute datetimestruct
@@ -1097,7 +1129,7 @@ def is_date_array_normalized(ndarray[int64_t] stamps, tz=None):
10971129
Py_ssize_t i, n = len(stamps)
10981130
ndarray[int64_t] trans, deltas
10991131
pandas_datetimestruct dts
1100-
datetime dt
1132+
int64_t local_val
11011133

11021134
if tz is None or is_utc(tz):
11031135
for i in range(n):
@@ -1106,11 +1138,9 @@ def is_date_array_normalized(ndarray[int64_t] stamps, tz=None):
11061138
return False
11071139
elif is_tzlocal(tz):
11081140
for i in range(n):
1109-
dt64_to_dtstruct(stamps[i], &dts)
1110-
dt = datetime(dts.year, dts.month, dts.day, dts.hour, dts.min,
1111-
dts.sec, dts.us, tz)
1112-
dt = dt + tz.utcoffset(dt)
1113-
if (dt.hour + dt.minute + dt.second + dt.microsecond) > 0:
1141+
local_val = tz_convert_utc_to_tzlocal(stamps[i], tz)
1142+
dt64_to_dtstruct(local_val, &dts)
1143+
if (dts.hour + dts.min + dts.sec + dts.us) > 0:
11141144
return False
11151145
else:
11161146
trans, deltas, typ = get_dst_info(tz)

pandas/_libs/tslibs/period.pyx

+4-5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ cimport ccalendar
3939
from ccalendar cimport dayofweek, get_day_of_year
4040
from ccalendar import MONTH_NUMBERS
4141
from ccalendar cimport is_leapyear
42+
from conversion cimport tz_convert_utc_to_tzlocal
4243
from frequencies cimport (get_freq_code, get_base_alias,
4344
get_to_timestamp_base, get_freq_str,
4445
get_rule_month)
@@ -591,6 +592,7 @@ cdef ndarray[int64_t] localize_dt64arr_to_period(ndarray[int64_t] stamps,
591592
ndarray[int64_t] result = np.empty(n, dtype=np.int64)
592593
ndarray[int64_t] trans, deltas, pos
593594
pandas_datetimestruct dts
595+
int64_t local_val
594596

595597
if is_utc(tz):
596598
for i in range(n):
@@ -607,11 +609,8 @@ cdef ndarray[int64_t] localize_dt64arr_to_period(ndarray[int64_t] stamps,
607609
if stamps[i] == NPY_NAT:
608610
result[i] = NPY_NAT
609611
continue
610-
dt64_to_dtstruct(stamps[i], &dts)
611-
dt = datetime(dts.year, dts.month, dts.day, dts.hour,
612-
dts.min, dts.sec, dts.us, tz)
613-
delta = int(get_utcoffset(tz, dt).total_seconds()) * 1000000000
614-
dt64_to_dtstruct(stamps[i] + delta, &dts)
612+
local_val = tz_convert_utc_to_tzlocal(stamps[i], tz)
613+
dt64_to_dtstruct(local_val, &dts)
615614
result[i] = get_period_ordinal(dts.year, dts.month, dts.day,
616615
dts.hour, dts.min, dts.sec,
617616
dts.us, dts.ps, freq)

pandas/_libs/tslibs/resolution.pyx

+4-5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ from timezones cimport (is_utc, is_tzlocal,
2323
maybe_get_tz, get_dst_info, get_utcoffset)
2424
from fields import build_field_sarray
2525
from conversion import tz_convert
26+
from conversion cimport tz_convert_utc_to_tzlocal
2627
from ccalendar import MONTH_ALIASES, int_to_weekday
2728

2829
from pandas._libs.properties import cache_readonly
@@ -78,6 +79,7 @@ cdef _reso_local(ndarray[int64_t] stamps, object tz):
7879
int reso = RESO_DAY, curr_reso
7980
ndarray[int64_t] trans, deltas, pos
8081
pandas_datetimestruct dts
82+
int64_t local_val
8183

8284
if is_utc(tz):
8385
for i in range(n):
@@ -91,11 +93,8 @@ cdef _reso_local(ndarray[int64_t] stamps, object tz):
9193
for i in range(n):
9294
if stamps[i] == NPY_NAT:
9395
continue
94-
dt64_to_dtstruct(stamps[i], &dts)
95-
dt = datetime(dts.year, dts.month, dts.day, dts.hour,
96-
dts.min, dts.sec, dts.us, tz)
97-
delta = int(get_utcoffset(tz, dt).total_seconds()) * 1000000000
98-
dt64_to_dtstruct(stamps[i] + delta, &dts)
96+
local_val = tz_convert_utc_to_tzlocal(stamps[i], tz)
97+
dt64_to_dtstruct(local_val, &dts)
9998
curr_reso = _reso_stamp(&dts)
10099
if curr_reso < reso:
101100
reso = curr_reso

0 commit comments

Comments
 (0)