@@ -58,6 +58,7 @@ cdef class Localizer:
5858 Py_ssize_t ntrans
5959 const int64_t[::1 ] deltas
6060 int64_t delta
61+ int64_t* tdata
6162
6263 @ cython.initializedcheck (False )
6364 @ cython.boundscheck (False )
@@ -68,6 +69,7 @@ cdef class Localizer:
6869 self .ntrans = - 1 # placeholder
6970 self .delta = - 1 # placeholder
7071 self .deltas = _deltas_placeholder
72+ self .tdata = NULL
7173
7274 if is_utc(tz) or tz is None :
7375 self .use_utc = True
@@ -90,6 +92,57 @@ cdef class Localizer:
9092 if typ == " pytz" :
9193 self .use_pytz = True
9294
95+ self .tdata = < int64_t* > cnp.PyArray_DATA(self .trans)
96+
97+
98+ @ cython.boundscheck (False )
99+ @ cython.wraparound (False )
100+ def tz_convert_from_utc (const int64_t[:] stamps , tzinfo tz ):
101+ """
102+ Convert the values (in i8) from UTC to tz
103+
104+ Parameters
105+ ----------
106+ stamps : ndarray[int64]
107+ tz : tzinfo
108+
109+ Returns
110+ -------
111+ ndarray[int64]
112+ """
113+ cdef:
114+ Localizer info = Localizer(tz)
115+ int64_t utc_val, local_val
116+ Py_ssize_t pos, i, n = stamps.shape[0 ]
117+
118+ int64_t[::1 ] result
119+
120+ if tz is None or is_utc(tz) or stamps.size == 0 :
121+ # Much faster than going through the "standard" pattern below
122+ return stamps.base.copy()
123+
124+ result = np.empty(n, dtype = np.int64)
125+
126+ for i in range (n):
127+ utc_val = stamps[i]
128+ if utc_val == NPY_NAT:
129+ result[i] = NPY_NAT
130+ continue
131+
132+ if info.use_utc:
133+ local_val = utc_val
134+ elif info.use_tzlocal:
135+ local_val = utc_val + localize_tzinfo_api(utc_val, tz)
136+ elif info.use_fixed:
137+ local_val = utc_val + info.delta
138+ else :
139+ pos = bisect_right_i8(info.tdata, utc_val, info.ntrans) - 1
140+ local_val = utc_val + info.deltas[pos]
141+
142+ result[i] = local_val
143+
144+ return result.base
145+
93146
94147# -------------------------------------------------------------------------
95148
@@ -134,7 +187,6 @@ def ints_to_pydatetime(
134187 Localizer info = Localizer(tz)
135188 int64_t utc_val , local_val
136189 Py_ssize_t pos , i , n = stamps.shape[0 ]
137- int64_t* tdata = NULL
138190
139191 npy_datetimestruct dts
140192 tzinfo new_tz
@@ -155,9 +207,6 @@ def ints_to_pydatetime(
155207 "box must be one of 'datetime', 'date', 'time' or 'timestamp'"
156208 )
157209
158- if info.use_dst:
159- tdata = < int64_t* > cnp.PyArray_DATA(info.trans)
160-
161210 for i in range(n ):
162211 utc_val = stamps[i]
163212 new_tz = tz
@@ -173,7 +222,7 @@ def ints_to_pydatetime(
173222 elif info.use_fixed:
174223 local_val = utc_val + info.delta
175224 else :
176- pos = bisect_right_i8(tdata, utc_val, info.ntrans) - 1
225+ pos = bisect_right_i8(info. tdata, utc_val, info.ntrans) - 1
177226 local_val = utc_val + info.deltas[pos]
178227
179228 if info.use_pytz:
@@ -221,14 +270,10 @@ def get_resolution(const int64_t[:] stamps, tzinfo tz=None) -> Resolution:
221270 Localizer info = Localizer(tz)
222271 int64_t utc_val , local_val
223272 Py_ssize_t pos , i , n = stamps.shape[0 ]
224- int64_t* tdata = NULL
225273
226274 npy_datetimestruct dts
227275 c_Resolution reso = c_Resolution.RESO_DAY, curr_reso
228276
229- if info.use_dst:
230- tdata = < int64_t* > cnp.PyArray_DATA(info.trans)
231-
232277 for i in range(n ):
233278 utc_val = stamps[i]
234279 if utc_val == NPY_NAT:
@@ -241,7 +286,7 @@ def get_resolution(const int64_t[:] stamps, tzinfo tz=None) -> Resolution:
241286 elif info.use_fixed:
242287 local_val = utc_val + info.delta
243288 else :
244- pos = bisect_right_i8(tdata, utc_val, info.ntrans) - 1
289+ pos = bisect_right_i8(info. tdata, utc_val, info.ntrans) - 1
245290 local_val = utc_val + info.deltas[pos]
246291
247292 dt64_to_dtstruct(local_val, & dts)
@@ -277,13 +322,9 @@ cpdef ndarray[int64_t] normalize_i8_timestamps(const int64_t[:] stamps, tzinfo t
277322 Localizer info = Localizer(tz)
278323 int64_t utc_val, local_val
279324 Py_ssize_t pos, i, n = stamps.shape[0 ]
280- int64_t* tdata = NULL
281325
282326 int64_t[::1 ] result = np.empty(n, dtype = np.int64)
283327
284- if info.use_dst:
285- tdata = < int64_t* > cnp.PyArray_DATA(info.trans)
286-
287328 for i in range (n):
288329 utc_val = stamps[i]
289330 if utc_val == NPY_NAT:
@@ -297,7 +338,7 @@ cpdef ndarray[int64_t] normalize_i8_timestamps(const int64_t[:] stamps, tzinfo t
297338 elif info.use_fixed:
298339 local_val = utc_val + info.delta
299340 else :
300- pos = bisect_right_i8(tdata, utc_val, info.ntrans) - 1
341+ pos = bisect_right_i8(info. tdata, utc_val, info.ntrans) - 1
301342 local_val = utc_val + info.deltas[pos]
302343
303344 result[i] = local_val - (local_val % DAY_NANOS)
@@ -326,10 +367,6 @@ def is_date_array_normalized(const int64_t[:] stamps, tzinfo tz=None) -> bool:
326367 Localizer info = Localizer(tz)
327368 int64_t utc_val , local_val
328369 Py_ssize_t pos , i , n = stamps.shape[0 ]
329- int64_t* tdata = NULL
330-
331- if info.use_dst:
332- tdata = < int64_t* > cnp.PyArray_DATA(info.trans)
333370
334371 for i in range(n ):
335372 utc_val = stamps[i]
@@ -340,7 +377,7 @@ def is_date_array_normalized(const int64_t[:] stamps, tzinfo tz=None) -> bool:
340377 elif info.use_fixed:
341378 local_val = utc_val + info.delta
342379 else :
343- pos = bisect_right_i8(tdata, utc_val, info.ntrans) - 1
380+ pos = bisect_right_i8(info. tdata, utc_val, info.ntrans) - 1
344381 local_val = utc_val + info.deltas[pos]
345382
346383 if local_val % DAY_NANOS != 0 :
@@ -360,15 +397,11 @@ def dt64arr_to_periodarr(ndarray stamps, int freq, tzinfo tz):
360397 Localizer info = Localizer(tz)
361398 Py_ssize_t pos, i, n = stamps.size
362399 int64_t utc_val, local_val, res_val
363- int64_t* tdata = NULL
364400
365401 npy_datetimestruct dts
366402 ndarray result = cnp.PyArray_EMPTY(stamps.ndim, stamps.shape, cnp.NPY_INT64, 0 )
367403 cnp.broadcast mi = cnp.PyArray_MultiIterNew2(result, stamps)
368404
369- if info.use_dst:
370- tdata = < int64_t* > cnp.PyArray_DATA(info.trans)
371-
372405 for i in range (n):
373406 # Analogous to: utc_val = stamps[i]
374407 utc_val = (< int64_t* > cnp.PyArray_MultiIter_DATA(mi, 1 ))[0 ]
@@ -383,7 +416,7 @@ def dt64arr_to_periodarr(ndarray stamps, int freq, tzinfo tz):
383416 elif info.use_fixed:
384417 local_val = utc_val + info.delta
385418 else :
386- pos = bisect_right_i8(tdata, utc_val, info.ntrans) - 1
419+ pos = bisect_right_i8(info. tdata, utc_val, info.ntrans) - 1
387420 local_val = utc_val + info.deltas[pos]
388421
389422 dt64_to_dtstruct(local_val, & dts)
0 commit comments