Skip to content

Commit 2b79972

Browse files
jbrockmendelyehoshuadimarsky
authored andcommitted
REF: simplify tzconversion (pandas-dev#47019)
1 parent bf9ea1d commit 2b79972

File tree

7 files changed

+35
-72
lines changed

7 files changed

+35
-72
lines changed

pandas/_libs/tslibs/__init__.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,7 @@
5858
)
5959
from pandas._libs.tslibs.timestamps import Timestamp
6060
from pandas._libs.tslibs.timezones import tz_compare
61-
from pandas._libs.tslibs.tzconversion import (
62-
py_tz_convert_from_utc_single as tz_convert_from_utc_single,
63-
)
61+
from pandas._libs.tslibs.tzconversion import tz_convert_from_utc_single
6462
from pandas._libs.tslibs.vectorized import (
6563
dt64arr_to_periodarr,
6664
get_resolution,

pandas/_libs/tslibs/offsets.pyx

+3-9
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,7 @@ from pandas._libs.tslibs.ccalendar cimport (
5454
get_firstbday,
5555
get_lastbday,
5656
)
57-
from pandas._libs.tslibs.conversion cimport (
58-
convert_datetime_to_tsobject,
59-
localize_pydatetime,
60-
)
57+
from pandas._libs.tslibs.conversion cimport localize_pydatetime
6158
from pandas._libs.tslibs.nattype cimport (
6259
NPY_NAT,
6360
c_NaT as NaT,
@@ -68,7 +65,6 @@ from pandas._libs.tslibs.np_datetime cimport (
6865
npy_datetimestruct,
6966
pydate_to_dtstruct,
7067
)
71-
from pandas._libs.tslibs.tzconversion cimport tz_convert_from_utc_single
7268

7369
from .dtypes cimport PeriodDtypeCode
7470
from .timedeltas cimport (
@@ -270,10 +266,8 @@ cdef _to_dt64D(dt):
270266
if getattr(dt, 'tzinfo', None) is not None:
271267
# Get the nanosecond timestamp,
272268
# equiv `Timestamp(dt).value` or `dt.timestamp() * 10**9`
273-
nanos = getattr(dt, "nanosecond", 0)
274-
i8 = convert_datetime_to_tsobject(dt, tz=None, nanos=nanos).value
275-
dt = tz_convert_from_utc_single(i8, dt.tzinfo)
276-
dt = np.int64(dt).astype('datetime64[ns]')
269+
naive = dt.astimezone(None)
270+
dt = np.datetime64(naive, "D")
277271
else:
278272
dt = np.datetime64(dt)
279273
if dt.dtype.name != "datetime64[D]":

pandas/_libs/tslibs/timestamps.pyx

+12-15
Original file line numberDiff line numberDiff line change
@@ -1979,22 +1979,19 @@ default 'raise'
19791979
value = tz_localize_to_utc_single(self.value, tz,
19801980
ambiguous=ambiguous,
19811981
nonexistent=nonexistent)
1982-
out = Timestamp(value, tz=tz)
1983-
if out is not NaT:
1984-
out._set_freq(self._freq) # avoid warning in constructor
1985-
return out
1982+
elif tz is None:
1983+
# reset tz
1984+
value = tz_convert_from_utc_single(self.value, self.tz)
1985+
19861986
else:
1987-
if tz is None:
1988-
# reset tz
1989-
value = tz_convert_from_utc_single(self.value, self.tz)
1990-
out = Timestamp(value, tz=tz)
1991-
if out is not NaT:
1992-
out._set_freq(self._freq) # avoid warning in constructor
1993-
return out
1994-
else:
1995-
raise TypeError(
1996-
"Cannot localize tz-aware Timestamp, use tz_convert for conversions"
1997-
)
1987+
raise TypeError(
1988+
"Cannot localize tz-aware Timestamp, use tz_convert for conversions"
1989+
)
1990+
1991+
out = Timestamp(value, tz=tz)
1992+
if out is not NaT:
1993+
out._set_freq(self._freq) # avoid warning in constructor
1994+
return out
19981995

19991996
def tz_convert(self, tz):
20001997
"""

pandas/_libs/tslibs/tzconversion.pxd

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ from numpy cimport (
66
)
77

88

9-
cdef int64_t tz_convert_from_utc_single(
10-
int64_t utc_val, tzinfo tz, bint* fold=?, Py_ssize_t* outpos=?
9+
cpdef int64_t tz_convert_from_utc_single(
10+
int64_t utc_val, tzinfo tz
1111
) except? -1
1212
cdef int64_t tz_localize_to_utc_single(
1313
int64_t val, tzinfo tz, object ambiguous=*, object nonexistent=*

pandas/_libs/tslibs/tzconversion.pyi

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import numpy as np
88

99
from pandas._typing import npt
1010

11-
# py_tz_convert_from_utc_single exposed for testing
12-
def py_tz_convert_from_utc_single(val: np.int64, tz: tzinfo) -> np.int64: ...
11+
# tz_convert_from_utc_single exposed for testing
12+
def tz_convert_from_utc_single(val: np.int64, tz: tzinfo) -> np.int64: ...
1313
def tz_localize_to_utc(
1414
vals: npt.NDArray[np.int64],
1515
tz: tzinfo | None,

pandas/_libs/tslibs/tzconversion.pyx

+14-40
Original file line numberDiff line numberDiff line change
@@ -183,29 +183,28 @@ timedelta-like}
183183
localized : ndarray[int64_t]
184184
"""
185185
cdef:
186-
const int64_t[::1] deltas
187186
ndarray[uint8_t, cast=True] ambiguous_array
188-
Py_ssize_t i, idx, pos, ntrans, n = vals.shape[0]
187+
Py_ssize_t i, idx, pos, n = vals.shape[0]
189188
Py_ssize_t delta_idx_offset, delta_idx, pos_left, pos_right
190-
int64_t *tdata
191189
int64_t v, left, right, val, new_local, remaining_mins
192190
int64_t first_delta, delta
193191
int64_t shift_delta = 0
194-
ndarray[int64_t] trans, result_a, result_b, dst_hours
192+
ndarray[int64_t] result_a, result_b, dst_hours
195193
int64_t[::1] result
196194
npy_datetimestruct dts
197195
bint infer_dst = False, is_dst = False, fill = False
198196
bint shift_forward = False, shift_backward = False
199197
bint fill_nonexist = False
200198
str stamp
199+
Localizer info = Localizer(tz)
201200

202201
# Vectorized version of DstTzInfo.localize
203-
if is_utc(tz) or tz is None:
202+
if info.use_utc:
204203
return vals.copy()
205204

206205
result = cnp.PyArray_EMPTY(vals.ndim, vals.shape, cnp.NPY_INT64, 0)
207206

208-
if is_tzlocal(tz) or is_zoneinfo(tz):
207+
if info.use_tzlocal:
209208
for i in range(n):
210209
v = vals[i]
211210
if v == NPY_NAT:
@@ -214,9 +213,8 @@ timedelta-like}
214213
result[i] = v - _tz_localize_using_tzinfo_api(v, tz, to_utc=True)
215214
return result.base # to return underlying ndarray
216215

217-
elif is_fixed_offset(tz):
218-
_, deltas, _ = get_dst_info(tz)
219-
delta = deltas[0]
216+
elif info.use_fixed:
217+
delta = info.delta
220218
for i in range(n):
221219
v = vals[i]
222220
if v == NPY_NAT:
@@ -259,14 +257,9 @@ timedelta-like}
259257
"shift_backwards} or a timedelta object")
260258
raise ValueError(msg)
261259

262-
trans, deltas, _ = get_dst_info(tz)
263-
264-
tdata = <int64_t*>cnp.PyArray_DATA(trans)
265-
ntrans = trans.shape[0]
266-
267260
# Determine whether each date lies left of the DST transition (store in
268261
# result_a) or right of the DST transition (store in result_b)
269-
result_a, result_b =_get_utc_bounds(vals, tdata, ntrans, deltas)
262+
result_a, result_b =_get_utc_bounds(vals, info.tdata, info.ntrans, info.deltas)
270263

271264
# silence false-positive compiler warning
272265
dst_hours = np.empty(0, dtype=np.int64)
@@ -278,7 +271,7 @@ timedelta-like}
278271
# Shift the delta_idx by if the UTC offset of
279272
# the target tz is greater than 0 and we're moving forward
280273
# or vice versa
281-
first_delta = deltas[0]
274+
first_delta = info.deltas[0]
282275
if (shift_forward or shift_delta > 0) and first_delta > 0:
283276
delta_idx_offset = 1
284277
elif (shift_backward or shift_delta < 0) and first_delta < 0:
@@ -336,10 +329,10 @@ timedelta-like}
336329
# nonexistent times
337330
new_local = val - remaining_mins - 1
338331

339-
delta_idx = bisect_right_i8(tdata, new_local, ntrans)
332+
delta_idx = bisect_right_i8(info.tdata, new_local, info.ntrans)
340333

341334
delta_idx = delta_idx - delta_idx_offset
342-
result[i] = new_local - deltas[delta_idx]
335+
result[i] = new_local - info.deltas[delta_idx]
343336
elif fill_nonexist:
344337
result[i] = NPY_NAT
345338
else:
@@ -519,19 +512,7 @@ cdef ndarray[int64_t] _get_dst_hours(
519512
# ----------------------------------------------------------------------
520513
# Timezone Conversion
521514

522-
def py_tz_convert_from_utc_single(int64_t utc_val, tzinfo tz):
523-
# The 'bint* fold=NULL' in tz_convert_from_utc_single means we cannot
524-
# make it cdef, so this is version exposed for testing from python.
525-
return tz_convert_from_utc_single(utc_val, tz)
526-
527-
528-
@cython.boundscheck(False)
529-
cdef int64_t tz_convert_from_utc_single(
530-
int64_t utc_val,
531-
tzinfo tz,
532-
bint* fold=NULL,
533-
Py_ssize_t* outpos=NULL,
534-
) except? -1:
515+
cpdef int64_t tz_convert_from_utc_single(int64_t utc_val, tzinfo tz) except? -1:
535516
"""
536517
Convert the val (in i8) from UTC to tz
537518
@@ -541,8 +522,6 @@ cdef int64_t tz_convert_from_utc_single(
541522
----------
542523
utc_val : int64
543524
tz : tzinfo
544-
fold : bint*, default NULL
545-
outpos : Py_ssize_t*, default NULL
546525
547526
Returns
548527
-------
@@ -552,13 +531,8 @@ cdef int64_t tz_convert_from_utc_single(
552531
Localizer info = Localizer(tz)
553532
Py_ssize_t pos
554533

555-
if utc_val == NPY_NAT:
556-
return utc_val
557-
558-
if outpos is not NULL and info.use_pytz:
559-
return info.utc_val_to_local_val(utc_val, outpos, fold)
560-
else:
561-
return info.utc_val_to_local_val(utc_val, &pos, fold)
534+
# Note: caller is responsible for ensuring utc_val != NPY_NAT
535+
return info.utc_val_to_local_val(utc_val, &pos)
562536

563537

564538
# OSError may be thrown by tzlocal on windows at or close to 1970-01-01

pandas/tests/tslibs/test_conversion.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
def _compare_utc_to_local(tz_didx):
2424
def f(x):
25-
return tzconversion.py_tz_convert_from_utc_single(x, tz_didx.tz)
25+
return tzconversion.tz_convert_from_utc_single(x, tz_didx.tz)
2626

2727
result = tz_convert_from_utc(tz_didx.asi8, tz_didx.tz)
2828
expected = np.vectorize(f)(tz_didx.asi8)

0 commit comments

Comments
 (0)