@@ -32,10 +32,7 @@ from .np_datetime cimport (
32
32
)
33
33
from .offsets cimport BaseOffset
34
34
from .period cimport get_period_ordinal
35
- from .timestamps cimport (
36
- create_timestamp_from_ts,
37
- normalize_i8_stamp,
38
- )
35
+ from .timestamps cimport create_timestamp_from_ts
39
36
from .timezones cimport (
40
37
get_dst_info,
41
38
is_tzlocal,
@@ -47,6 +44,54 @@ from .tzconversion cimport (
47
44
localize_tzinfo_api,
48
45
)
49
46
47
+
48
+ cdef const int64_t[::1 ] _deltas_placeholder = np.array([], dtype = np.int64)
49
+
50
+
51
+ @ cython.freelist (16 )
52
+ @cython.internal
53
+ @cython.final
54
+ cdef class Localizer:
55
+ cdef:
56
+ tzinfo tz
57
+ bint use_utc, use_fixed, use_tzlocal, use_dst, use_pytz
58
+ ndarray trans
59
+ Py_ssize_t ntrans
60
+ const int64_t[::1 ] deltas
61
+ int64_t delta
62
+
63
+ @ cython.initializedcheck (False )
64
+ @ cython.boundscheck (False )
65
+ def __cinit__ (self , tzinfo tz ):
66
+ self .tz = tz
67
+ self .use_utc = self .use_tzlocal = self .use_fixed = False
68
+ self .use_dst = self .use_pytz = False
69
+ self .ntrans = - 1 # placeholder
70
+ self .delta = - 1 # placeholder
71
+ self .deltas = _deltas_placeholder
72
+
73
+ if is_utc(tz) or tz is None :
74
+ self .use_utc = True
75
+
76
+ elif is_tzlocal(tz) or is_zoneinfo(tz):
77
+ self .use_tzlocal = True
78
+
79
+ else :
80
+ trans, deltas, typ = get_dst_info(tz)
81
+ self .trans = trans
82
+ self .ntrans = trans.shape[0 ]
83
+ self .deltas = deltas
84
+
85
+ if typ != " pytz" and typ != " dateutil" :
86
+ # static/fixed; in this case we know that len(delta) == 1
87
+ self .use_fixed = True
88
+ self .delta = deltas[0 ]
89
+ else :
90
+ self .use_dst = True
91
+ if typ == " pytz" :
92
+ self .use_pytz = True
93
+
94
+
50
95
# -------------------------------------------------------------------------
51
96
52
97
@@ -87,19 +132,14 @@ def ints_to_pydatetime(
87
132
ndarray[object] of type specified by box
88
133
"""
89
134
cdef:
90
- Py_ssize_t i , ntrans = - 1 , n = stamps.shape[ 0 ]
91
- ndarray[ int64_t] trans
92
- int64_t[::1] deltas
135
+ Localizer info = Localizer(tz)
136
+ int64_t utc_val , local_val
137
+ Py_ssize_t pos , i , n = stamps.shape[ 0 ]
93
138
int64_t* tdata = NULL
94
- intp_t pos
95
- int64_t utc_val , local_val , delta = NPY_NAT
96
- bint use_utc = False , use_tzlocal = False , use_fixed = False
97
- str typ
98
139
99
140
npy_datetimestruct dts
100
141
tzinfo new_tz
101
142
ndarray[object] result = np.empty(n, dtype = object )
102
- bint use_pytz = False
103
143
bint use_date = False , use_time = False , use_ts = False , use_pydt = False
104
144
105
145
if box == "date":
@@ -116,20 +156,8 @@ def ints_to_pydatetime(
116
156
"box must be one of 'datetime', 'date', 'time' or 'timestamp'"
117
157
)
118
158
119
- if is_utc(tz ) or tz is None:
120
- use_utc = True
121
- elif is_tzlocal(tz ) or is_zoneinfo(tz ):
122
- use_tzlocal = True
123
- else :
124
- trans, deltas, typ = get_dst_info(tz)
125
- ntrans = trans.shape[0 ]
126
- if typ not in [" pytz" , " dateutil" ]:
127
- # static/fixed; in this case we know that len(delta) == 1
128
- use_fixed = True
129
- delta = deltas[0 ]
130
- else :
131
- tdata = < int64_t* > cnp.PyArray_DATA(trans)
132
- use_pytz = typ == " pytz"
159
+ if info.use_dst:
160
+ tdata = < int64_t* > cnp.PyArray_DATA(info.trans)
133
161
134
162
for i in range(n ):
135
163
utc_val = stamps[i]
@@ -139,17 +167,17 @@ def ints_to_pydatetime(
139
167
result[i] = < object > NaT
140
168
continue
141
169
142
- if use_utc:
170
+ if info. use_utc:
143
171
local_val = utc_val
144
- elif use_tzlocal:
172
+ elif info. use_tzlocal:
145
173
local_val = utc_val + localize_tzinfo_api(utc_val, tz)
146
- elif use_fixed:
147
- local_val = utc_val + delta
174
+ elif info. use_fixed:
175
+ local_val = utc_val + info. delta
148
176
else :
149
- pos = bisect_right_i8(tdata, utc_val, ntrans) - 1
150
- local_val = utc_val + deltas[pos]
177
+ pos = bisect_right_i8(tdata, utc_val, info. ntrans) - 1
178
+ local_val = utc_val + info. deltas[pos]
151
179
152
- if use_pytz:
180
+ if info. use_pytz:
153
181
# find right representation of dst etc in pytz timezone
154
182
new_tz = tz._tzinfos[tz._transition_info[pos]]
155
183
@@ -191,46 +219,31 @@ cdef inline c_Resolution _reso_stamp(npy_datetimestruct *dts):
191
219
@ cython.boundscheck (False )
192
220
def get_resolution (const int64_t[:] stamps , tzinfo tz = None ) -> Resolution:
193
221
cdef:
194
- Py_ssize_t i , ntrans = - 1 , n = stamps.shape[ 0 ]
195
- ndarray[ int64_t] trans
196
- int64_t[::1] deltas
222
+ Localizer info = Localizer(tz)
223
+ int64_t utc_val , local_val
224
+ Py_ssize_t pos , i , n = stamps.shape[ 0 ]
197
225
int64_t* tdata = NULL
198
- intp_t pos
199
- int64_t utc_val , local_val , delta = NPY_NAT
200
- bint use_utc = False , use_tzlocal = False , use_fixed = False
201
- str typ
202
226
203
227
npy_datetimestruct dts
204
228
c_Resolution reso = c_Resolution.RESO_DAY, curr_reso
205
229
206
- if is_utc(tz ) or tz is None:
207
- use_utc = True
208
- elif is_tzlocal(tz ) or is_zoneinfo(tz ):
209
- use_tzlocal = True
210
- else :
211
- trans, deltas, typ = get_dst_info(tz)
212
- ntrans = trans.shape[0 ]
213
- if typ not in [" pytz" , " dateutil" ]:
214
- # static/fixed; in this case we know that len(delta) == 1
215
- use_fixed = True
216
- delta = deltas[0 ]
217
- else :
218
- tdata = < int64_t* > cnp.PyArray_DATA(trans)
230
+ if info.use_dst:
231
+ tdata = < int64_t* > cnp.PyArray_DATA(info.trans)
219
232
220
233
for i in range(n ):
221
234
utc_val = stamps[i]
222
235
if utc_val == NPY_NAT:
223
236
continue
224
237
225
- if use_utc:
238
+ if info. use_utc:
226
239
local_val = utc_val
227
- elif use_tzlocal:
240
+ elif info. use_tzlocal:
228
241
local_val = utc_val + localize_tzinfo_api(utc_val, tz)
229
- elif use_fixed:
230
- local_val = utc_val + delta
242
+ elif info. use_fixed:
243
+ local_val = utc_val + info. delta
231
244
else :
232
- pos = bisect_right_i8(tdata, utc_val, ntrans) - 1
233
- local_val = utc_val + deltas[pos]
245
+ pos = bisect_right_i8(tdata, utc_val, info. ntrans) - 1
246
+ local_val = utc_val + info. deltas[pos]
234
247
235
248
dt64_to_dtstruct(local_val, & dts)
236
249
curr_reso = _reso_stamp(& dts)
@@ -242,6 +255,8 @@ def get_resolution(const int64_t[:] stamps, tzinfo tz=None) -> Resolution:
242
255
243
256
# -------------------------------------------------------------------------
244
257
258
+
259
+ @ cython.cdivision (False )
245
260
@ cython.wraparound (False )
246
261
@ cython.boundscheck (False )
247
262
cpdef ndarray[int64_t] normalize_i8_timestamps(const int64_t[:] stamps, tzinfo tz):
@@ -260,48 +275,33 @@ cpdef ndarray[int64_t] normalize_i8_timestamps(const int64_t[:] stamps, tzinfo t
260
275
result : int64 ndarray of converted of normalized nanosecond timestamps
261
276
"""
262
277
cdef:
263
- Py_ssize_t i, ntrans = - 1 , n = stamps.shape[ 0 ]
264
- ndarray[ int64_t] trans
265
- int64_t[:: 1 ] deltas
278
+ Localizer info = Localizer(tz)
279
+ int64_t utc_val, local_val
280
+ Py_ssize_t pos, i, n = stamps.shape[ 0 ]
266
281
int64_t* tdata = NULL
267
- intp_t pos
268
- int64_t utc_val, local_val, delta = NPY_NAT
269
- bint use_utc = False , use_tzlocal = False , use_fixed = False
270
- str typ
271
282
272
283
int64_t[::1 ] result = np.empty(n, dtype = np.int64)
273
284
274
- if is_utc(tz) or tz is None :
275
- use_utc = True
276
- elif is_tzlocal(tz) or is_zoneinfo(tz):
277
- use_tzlocal = True
278
- else :
279
- trans, deltas, typ = get_dst_info(tz)
280
- ntrans = trans.shape[0 ]
281
- if typ not in [" pytz" , " dateutil" ]:
282
- # static/fixed; in this case we know that len(delta) == 1
283
- use_fixed = True
284
- delta = deltas[0 ]
285
- else :
286
- tdata = < int64_t* > cnp.PyArray_DATA(trans)
285
+ if info.use_dst:
286
+ tdata = < int64_t* > cnp.PyArray_DATA(info.trans)
287
287
288
288
for i in range (n):
289
289
utc_val = stamps[i]
290
290
if utc_val == NPY_NAT:
291
291
result[i] = NPY_NAT
292
292
continue
293
293
294
- if use_utc:
294
+ if info. use_utc:
295
295
local_val = utc_val
296
- elif use_tzlocal:
296
+ elif info. use_tzlocal:
297
297
local_val = utc_val + localize_tzinfo_api(utc_val, tz)
298
- elif use_fixed:
299
- local_val = utc_val + delta
298
+ elif info. use_fixed:
299
+ local_val = utc_val + info. delta
300
300
else :
301
- pos = bisect_right_i8(tdata, utc_val, ntrans) - 1
302
- local_val = utc_val + deltas[pos]
301
+ pos = bisect_right_i8(tdata, utc_val, info. ntrans) - 1
302
+ local_val = utc_val + info. deltas[pos]
303
303
304
- result[i] = normalize_i8_stamp (local_val)
304
+ result[i] = local_val - (local_val % DAY_NANOS )
305
305
306
306
return result.base # `.base` to access underlying ndarray
307
307
@@ -324,40 +324,25 @@ def is_date_array_normalized(const int64_t[:] stamps, tzinfo tz=None) -> bool:
324
324
is_normalized : bool True if all stamps are normalized
325
325
"""
326
326
cdef:
327
- Py_ssize_t i , ntrans = - 1 , n = stamps.shape[ 0 ]
328
- ndarray[ int64_t] trans
329
- int64_t[::1] deltas
327
+ Localizer info = Localizer(tz)
328
+ int64_t utc_val , local_val
329
+ Py_ssize_t pos , i , n = stamps.shape[ 0 ]
330
330
int64_t* tdata = NULL
331
- intp_t pos
332
- int64_t utc_val , local_val , delta = NPY_NAT
333
- bint use_utc = False , use_tzlocal = False , use_fixed = False
334
- str typ
335
-
336
- if is_utc(tz ) or tz is None:
337
- use_utc = True
338
- elif is_tzlocal(tz ) or is_zoneinfo(tz ):
339
- use_tzlocal = True
340
- else :
341
- trans, deltas, typ = get_dst_info(tz)
342
- ntrans = trans.shape[0 ]
343
- if typ not in [" pytz" , " dateutil" ]:
344
- # static/fixed; in this case we know that len(delta) == 1
345
- use_fixed = True
346
- delta = deltas[0 ]
347
- else :
348
- tdata = < int64_t* > cnp.PyArray_DATA(trans)
331
+
332
+ if info.use_dst:
333
+ tdata = < int64_t* > cnp.PyArray_DATA(info.trans)
349
334
350
335
for i in range(n ):
351
336
utc_val = stamps[i]
352
- if use_utc:
337
+ if info. use_utc:
353
338
local_val = utc_val
354
- elif use_tzlocal:
339
+ elif info. use_tzlocal:
355
340
local_val = utc_val + localize_tzinfo_api(utc_val, tz)
356
- elif use_fixed:
357
- local_val = utc_val + delta
341
+ elif info. use_fixed:
342
+ local_val = utc_val + info. delta
358
343
else :
359
- pos = bisect_right_i8(tdata, utc_val, ntrans) - 1
360
- local_val = utc_val + deltas[pos]
344
+ pos = bisect_right_i8(tdata, utc_val, info. ntrans) - 1
345
+ local_val = utc_val + info. deltas[pos]
361
346
362
347
if local_val % DAY_NANOS != 0 :
363
348
return False
@@ -372,47 +357,32 @@ def is_date_array_normalized(const int64_t[:] stamps, tzinfo tz=None) -> bool:
372
357
@ cython.boundscheck (False )
373
358
def dt64arr_to_periodarr (const int64_t[:] stamps , int freq , tzinfo tz ):
374
359
cdef:
375
- Py_ssize_t i, ntrans = - 1 , n = stamps.shape[ 0 ]
376
- ndarray[ int64_t] trans
377
- int64_t[:: 1 ] deltas
360
+ Localizer info = Localizer(tz)
361
+ int64_t utc_val, local_val
362
+ Py_ssize_t pos, i, n = stamps.shape[ 0 ]
378
363
int64_t* tdata = NULL
379
- intp_t pos
380
- int64_t utc_val, local_val, delta = NPY_NAT
381
- bint use_utc = False , use_tzlocal = False , use_fixed = False
382
- str typ
383
364
384
365
npy_datetimestruct dts
385
366
int64_t[::1 ] result = np.empty(n, dtype = np.int64)
386
367
387
- if is_utc(tz) or tz is None :
388
- use_utc = True
389
- elif is_tzlocal(tz) or is_zoneinfo(tz):
390
- use_tzlocal = True
391
- else :
392
- trans, deltas, typ = get_dst_info(tz)
393
- ntrans = trans.shape[0 ]
394
- if typ not in [" pytz" , " dateutil" ]:
395
- # static/fixed; in this case we know that len(delta) == 1
396
- use_fixed = True
397
- delta = deltas[0 ]
398
- else :
399
- tdata = < int64_t* > cnp.PyArray_DATA(trans)
368
+ if info.use_dst:
369
+ tdata = < int64_t* > cnp.PyArray_DATA(info.trans)
400
370
401
371
for i in range (n):
402
372
utc_val = stamps[i]
403
373
if utc_val == NPY_NAT:
404
374
result[i] = NPY_NAT
405
375
continue
406
376
407
- if use_utc:
377
+ if info. use_utc:
408
378
local_val = utc_val
409
- elif use_tzlocal:
379
+ elif info. use_tzlocal:
410
380
local_val = utc_val + localize_tzinfo_api(utc_val, tz)
411
- elif use_fixed:
412
- local_val = utc_val + delta
381
+ elif info. use_fixed:
382
+ local_val = utc_val + info. delta
413
383
else :
414
- pos = bisect_right_i8(tdata, utc_val, ntrans) - 1
415
- local_val = utc_val + deltas[pos]
384
+ pos = bisect_right_i8(tdata, utc_val, info. ntrans) - 1
385
+ local_val = utc_val + info. deltas[pos]
416
386
417
387
dt64_to_dtstruct(local_val, & dts)
418
388
result[i] = get_period_ordinal(& dts, freq)
0 commit comments