@@ -9,12 +9,15 @@ from cpython.datetime cimport (
9
9
10
10
import numpy as np
11
11
12
+ cimport numpy as cnp
12
13
from numpy cimport (
13
14
int64_t,
14
15
intp_t,
15
16
ndarray,
16
17
)
17
18
19
+ cnp.import_array()
20
+
18
21
from .conversion cimport normalize_i8_stamp
19
22
20
23
from .dtypes import Resolution
@@ -35,52 +38,13 @@ from .timezones cimport (
35
38
is_tzlocal,
36
39
is_utc,
37
40
)
38
- from .tzconversion cimport tz_convert_utc_to_tzlocal
41
+ from .tzconversion cimport (
42
+ bisect_right_i8,
43
+ tz_convert_utc_to_tzlocal,
44
+ )
39
45
40
46
# -------------------------------------------------------------------------
41
47
42
- cdef inline object create_datetime_from_ts(
43
- int64_t value,
44
- npy_datetimestruct dts,
45
- tzinfo tz,
46
- object freq,
47
- bint fold,
48
- ):
49
- """
50
- Convenience routine to construct a datetime.datetime from its parts.
51
- """
52
- return datetime(
53
- dts.year, dts.month, dts.day, dts.hour, dts.min, dts.sec, dts.us,
54
- tz, fold = fold,
55
- )
56
-
57
-
58
- cdef inline object create_date_from_ts(
59
- int64_t value,
60
- npy_datetimestruct dts,
61
- tzinfo tz,
62
- object freq,
63
- bint fold
64
- ):
65
- """
66
- Convenience routine to construct a datetime.date from its parts.
67
- """
68
- # GH#25057 add fold argument to match other func_create signatures
69
- return date(dts.year, dts.month, dts.day)
70
-
71
-
72
- cdef inline object create_time_from_ts(
73
- int64_t value,
74
- npy_datetimestruct dts,
75
- tzinfo tz,
76
- object freq,
77
- bint fold
78
- ):
79
- """
80
- Convenience routine to construct a datetime.time from its parts.
81
- """
82
- return time(dts.hour, dts.min, dts.sec, dts.us, tz, fold = fold)
83
-
84
48
85
49
@ cython.wraparound (False )
86
50
@ cython.boundscheck (False )
@@ -119,29 +83,29 @@ def ints_to_pydatetime(
119
83
ndarray[object] of type specified by box
120
84
"""
121
85
cdef:
122
- Py_ssize_t i , n = len (stamps)
86
+ Py_ssize_t i , ntrans = - 1 , n = len (stamps)
123
87
ndarray[int64_t] trans
124
88
int64_t[::1] deltas
125
- intp_t[:] pos
89
+ int64_t* tdata = NULL
90
+ intp_t pos
126
91
npy_datetimestruct dts
127
92
object dt , new_tz
128
93
str typ
129
94
int64_t value , local_val , delta = NPY_NAT # dummy for delta
130
95
ndarray[object] result = np.empty(n, dtype = object )
131
- object (*func_create )(int64_t , npy_datetimestruct , tzinfo , object , bint )
132
96
bint use_utc = False , use_tzlocal = False , use_fixed = False
133
97
bint use_pytz = False
98
+ bint use_date = False , use_time = False , use_ts = False , use_pydt = False
134
99
135
100
if box == "date":
136
101
assert (tz is None ), "tz should be None when converting to date"
137
-
138
- func_create = create_date_from_ts
102
+ use_date = True
139
103
elif box == "timestamp":
140
- func_create = create_timestamp_from_ts
104
+ use_ts = True
141
105
elif box == "time":
142
- func_create = create_time_from_ts
106
+ use_time = True
143
107
elif box == "datetime":
144
- func_create = create_datetime_from_ts
108
+ use_pydt = True
145
109
else:
146
110
raise ValueError(
147
111
"box must be one of 'datetime', 'date', 'time' or 'timestamp'"
@@ -153,12 +117,13 @@ def ints_to_pydatetime(
153
117
use_tzlocal = True
154
118
else :
155
119
trans, deltas, typ = get_dst_info(tz)
120
+ ntrans = trans.shape[0 ]
156
121
if typ not in [" pytz" , " dateutil" ]:
157
122
# static/fixed; in this case we know that len(delta) == 1
158
123
use_fixed = True
159
124
delta = deltas[0 ]
160
125
else :
161
- pos = trans.searchsorted(stamps, side = " right " ) - 1
126
+ tdata = < int64_t * > cnp.PyArray_DATA(trans)
162
127
use_pytz = typ == " pytz"
163
128
164
129
for i in range (n):
@@ -176,14 +141,26 @@ def ints_to_pydatetime(
176
141
elif use_fixed:
177
142
local_val = value + delta
178
143
else :
179
- local_val = value + deltas[pos[i]]
144
+ pos = bisect_right_i8(tdata, value, ntrans) - 1
145
+ local_val = value + deltas[pos]
180
146
181
- if use_pytz:
182
- # find right representation of dst etc in pytz timezone
183
- new_tz = tz._tzinfos[tz._transition_info[pos[i] ]]
147
+ if use_pytz:
148
+ # find right representation of dst etc in pytz timezone
149
+ new_tz = tz._tzinfos[tz._transition_info[pos]]
184
150
185
151
dt64_to_dtstruct(local_val, & dts)
186
- result[i] = func_create(value, dts, new_tz, freq, fold)
152
+
153
+ if use_ts:
154
+ result[i] = create_timestamp_from_ts(value, dts, new_tz, freq, fold)
155
+ elif use_pydt:
156
+ result[i] = datetime(
157
+ dts.year, dts.month, dts.day, dts.hour, dts.min, dts.sec, dts.us,
158
+ new_tz, fold = fold,
159
+ )
160
+ elif use_date:
161
+ result[i] = date(dts.year, dts.month, dts.day)
162
+ else :
163
+ result[i] = time(dts.hour, dts.min, dts.sec, dts.us, new_tz, fold = fold)
187
164
188
165
return result
189
166
@@ -219,12 +196,13 @@ cdef inline int _reso_stamp(npy_datetimestruct *dts):
219
196
220
197
def get_resolution (const int64_t[:] stamps , tzinfo tz = None ) -> Resolution:
221
198
cdef:
222
- Py_ssize_t i , n = len (stamps)
199
+ Py_ssize_t i , ntrans = - 1 , n = len (stamps)
223
200
npy_datetimestruct dts
224
201
int reso = RESO_DAY, curr_reso
225
202
ndarray[int64_t] trans
226
203
int64_t[::1] deltas
227
- intp_t[:] pos
204
+ int64_t* tdata = NULL
205
+ intp_t pos
228
206
int64_t local_val , delta = NPY_NAT
229
207
bint use_utc = False , use_tzlocal = False , use_fixed = False
230
208
@@ -234,12 +212,13 @@ def get_resolution(const int64_t[:] stamps, tzinfo tz=None) -> Resolution:
234
212
use_tzlocal = True
235
213
else :
236
214
trans, deltas, typ = get_dst_info(tz)
215
+ ntrans = trans.shape[0 ]
237
216
if typ not in [" pytz" , " dateutil" ]:
238
217
# static/fixed; in this case we know that len(delta) == 1
239
218
use_fixed = True
240
219
delta = deltas[0 ]
241
220
else :
242
- pos = trans.searchsorted(stamps, side = " right " ) - 1
221
+ tdata = < int64_t * > cnp.PyArray_DATA(trans)
243
222
244
223
for i in range (n):
245
224
if stamps[i] == NPY_NAT:
@@ -252,7 +231,8 @@ def get_resolution(const int64_t[:] stamps, tzinfo tz=None) -> Resolution:
252
231
elif use_fixed:
253
232
local_val = stamps[i] + delta
254
233
else :
255
- local_val = stamps[i] + deltas[pos[i]]
234
+ pos = bisect_right_i8(tdata, stamps[i], ntrans) - 1
235
+ local_val = stamps[i] + deltas[pos]
256
236
257
237
dt64_to_dtstruct(local_val, & dts)
258
238
curr_reso = _reso_stamp(& dts)
@@ -282,12 +262,13 @@ cpdef ndarray[int64_t] normalize_i8_timestamps(const int64_t[:] stamps, tzinfo t
282
262
result : int64 ndarray of converted of normalized nanosecond timestamps
283
263
"""
284
264
cdef:
285
- Py_ssize_t i, n = len (stamps)
265
+ Py_ssize_t i, ntrans = - 1 , n = len (stamps)
286
266
int64_t[:] result = np.empty(n, dtype = np.int64)
287
267
ndarray[int64_t] trans
288
268
int64_t[::1 ] deltas
269
+ int64_t* tdata = NULL
289
270
str typ
290
- Py_ssize_t[:] pos
271
+ Py_ssize_t pos
291
272
int64_t local_val, delta = NPY_NAT
292
273
bint use_utc = False , use_tzlocal = False , use_fixed = False
293
274
@@ -297,12 +278,13 @@ cpdef ndarray[int64_t] normalize_i8_timestamps(const int64_t[:] stamps, tzinfo t
297
278
use_tzlocal = True
298
279
else :
299
280
trans, deltas, typ = get_dst_info(tz)
281
+ ntrans = trans.shape[0 ]
300
282
if typ not in [" pytz" , " dateutil" ]:
301
283
# static/fixed; in this case we know that len(delta) == 1
302
284
use_fixed = True
303
285
delta = deltas[0 ]
304
286
else :
305
- pos = trans.searchsorted(stamps, side = " right " ) - 1
287
+ tdata = < int64_t * > cnp.PyArray_DATA(trans)
306
288
307
289
for i in range (n):
308
290
if stamps[i] == NPY_NAT:
@@ -316,7 +298,8 @@ cpdef ndarray[int64_t] normalize_i8_timestamps(const int64_t[:] stamps, tzinfo t
316
298
elif use_fixed:
317
299
local_val = stamps[i] + delta
318
300
else :
319
- local_val = stamps[i] + deltas[pos[i]]
301
+ pos = bisect_right_i8(tdata, stamps[i], ntrans) - 1
302
+ local_val = stamps[i] + deltas[pos]
320
303
321
304
result[i] = normalize_i8_stamp(local_val)
322
305
@@ -341,10 +324,11 @@ def is_date_array_normalized(const int64_t[:] stamps, tzinfo tz=None) -> bool:
341
324
is_normalized : bool True if all stamps are normalized
342
325
"""
343
326
cdef:
344
- Py_ssize_t i , n = len (stamps)
327
+ Py_ssize_t i , ntrans = - 1 , n = len (stamps)
345
328
ndarray[int64_t] trans
346
329
int64_t[::1] deltas
347
- intp_t[:] pos
330
+ int64_t* tdata = NULL
331
+ intp_t pos
348
332
int64_t local_val , delta = NPY_NAT
349
333
str typ
350
334
int64_t day_nanos = 24 * 3600 * 1 _000_000_000
@@ -356,12 +340,13 @@ def is_date_array_normalized(const int64_t[:] stamps, tzinfo tz=None) -> bool:
356
340
use_tzlocal = True
357
341
else :
358
342
trans, deltas, typ = get_dst_info(tz)
343
+ ntrans = trans.shape[0 ]
359
344
if typ not in [" pytz" , " dateutil" ]:
360
345
# static/fixed; in this case we know that len(delta) == 1
361
346
use_fixed = True
362
347
delta = deltas[0 ]
363
348
else :
364
- pos = trans.searchsorted(stamps, side = " right " ) - 1
349
+ tdata = < int64_t * > cnp.PyArray_DATA(trans)
365
350
366
351
for i in range (n):
367
352
if use_utc:
@@ -371,7 +356,8 @@ def is_date_array_normalized(const int64_t[:] stamps, tzinfo tz=None) -> bool:
371
356
elif use_fixed:
372
357
local_val = stamps[i] + delta
373
358
else :
374
- local_val = stamps[i] + deltas[pos[i]]
359
+ pos = bisect_right_i8(tdata, stamps[i], ntrans) - 1
360
+ local_val = stamps[i] + deltas[pos]
375
361
376
362
if local_val % day_nanos != 0 :
377
363
return False
@@ -386,11 +372,12 @@ def is_date_array_normalized(const int64_t[:] stamps, tzinfo tz=None) -> bool:
386
372
@ cython.boundscheck (False )
387
373
def dt64arr_to_periodarr (const int64_t[:] stamps , int freq , tzinfo tz ):
388
374
cdef:
389
- Py_ssize_t i, n = len (stamps)
375
+ Py_ssize_t i, ntrans = - 1 , n = len (stamps)
390
376
int64_t[:] result = np.empty(n, dtype = np.int64)
391
377
ndarray[int64_t] trans
392
378
int64_t[::1 ] deltas
393
- Py_ssize_t[:] pos
379
+ int64_t* tdata = NULL
380
+ intp_t pos
394
381
npy_datetimestruct dts
395
382
int64_t local_val, delta = NPY_NAT
396
383
bint use_utc = False , use_tzlocal = False , use_fixed = False
@@ -401,12 +388,13 @@ def dt64arr_to_periodarr(const int64_t[:] stamps, int freq, tzinfo tz):
401
388
use_tzlocal = True
402
389
else :
403
390
trans, deltas, typ = get_dst_info(tz)
391
+ ntrans = trans.shape[0 ]
404
392
if typ not in [" pytz" , " dateutil" ]:
405
393
# static/fixed; in this case we know that len(delta) == 1
406
394
use_fixed = True
407
395
delta = deltas[0 ]
408
396
else :
409
- pos = trans.searchsorted(stamps, side = " right " ) - 1
397
+ tdata = < int64_t * > cnp.PyArray_DATA(trans)
410
398
411
399
for i in range (n):
412
400
if stamps[i] == NPY_NAT:
@@ -420,7 +408,8 @@ def dt64arr_to_periodarr(const int64_t[:] stamps, int freq, tzinfo tz):
420
408
elif use_fixed:
421
409
local_val = stamps[i] + delta
422
410
else :
423
- local_val = stamps[i] + deltas[pos[i]]
411
+ pos = bisect_right_i8(tdata, stamps[i], ntrans) - 1
412
+ local_val = stamps[i] + deltas[pos]
424
413
425
414
dt64_to_dtstruct(local_val, & dts)
426
415
result[i] = get_period_ordinal(& dts, freq)
0 commit comments