forked from pandas-dev/pandas
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnp_datetime.pyx
179 lines (138 loc) · 6.15 KB
/
np_datetime.pyx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
from cpython.object cimport Py_EQ, Py_NE, Py_GE, Py_GT, Py_LT, Py_LE
from cpython.datetime cimport (datetime, date,
PyDateTime_IMPORT,
PyDateTime_GET_YEAR, PyDateTime_GET_MONTH,
PyDateTime_GET_DAY, PyDateTime_DATE_GET_HOUR,
PyDateTime_DATE_GET_MINUTE,
PyDateTime_DATE_GET_SECOND,
PyDateTime_DATE_GET_MICROSECOND)
PyDateTime_IMPORT
from numpy cimport int64_t
from pandas._libs.tslibs.util cimport get_c_string_buf_and_size
cdef extern from "src/datetime/np_datetime.h":
int cmp_npy_datetimestruct(npy_datetimestruct *a,
npy_datetimestruct *b)
npy_datetime npy_datetimestruct_to_datetime(NPY_DATETIMEUNIT fr,
npy_datetimestruct *d) nogil
void pandas_datetime_to_datetimestruct(npy_datetime val,
NPY_DATETIMEUNIT fr,
npy_datetimestruct *result) nogil
void pandas_timedelta_to_timedeltastruct(npy_timedelta val,
NPY_DATETIMEUNIT fr,
pandas_timedeltastruct *result
) nogil
npy_datetimestruct _NS_MIN_DTS, _NS_MAX_DTS
cdef extern from "src/datetime/np_datetime_strings.h":
int parse_iso_8601_datetime(const char *str, int len, int want_exc,
npy_datetimestruct *out,
int *out_local, int *out_tzoffset)
# ----------------------------------------------------------------------
# numpy object inspection
cdef inline npy_datetime get_datetime64_value(object obj) nogil:
"""
returns the int64 value underlying scalar numpy datetime64 object
Note that to interpret this as a datetime, the corresponding unit is
also needed. That can be found using `get_datetime64_unit`.
"""
return (<PyDatetimeScalarObject*>obj).obval
cdef inline npy_timedelta get_timedelta64_value(object obj) nogil:
"""
returns the int64 value underlying scalar numpy timedelta64 object
"""
return (<PyTimedeltaScalarObject*>obj).obval
cdef inline NPY_DATETIMEUNIT get_datetime64_unit(object obj) nogil:
"""
returns the unit part of the dtype for a numpy datetime64 object.
"""
return <NPY_DATETIMEUNIT>(<PyDatetimeScalarObject*>obj).obmeta.base
# ----------------------------------------------------------------------
# Comparison
cdef int reverse_ops[6]
reverse_ops[Py_LT] = Py_GT
reverse_ops[Py_LE] = Py_GE
reverse_ops[Py_EQ] = Py_EQ
reverse_ops[Py_NE] = Py_NE
reverse_ops[Py_GT] = Py_LT
reverse_ops[Py_GE] = Py_LE
cdef inline bint cmp_scalar(int64_t lhs, int64_t rhs, int op) except -1:
"""
cmp_scalar is a more performant version of PyObject_RichCompare
typed for int64_t arguments.
"""
if op == Py_EQ:
return lhs == rhs
elif op == Py_NE:
return lhs != rhs
elif op == Py_LT:
return lhs < rhs
elif op == Py_LE:
return lhs <= rhs
elif op == Py_GT:
return lhs > rhs
elif op == Py_GE:
return lhs >= rhs
class OutOfBoundsDatetime(ValueError):
pass
cdef inline check_dts_bounds(npy_datetimestruct *dts):
"""Raises OutOfBoundsDatetime if the given date is outside the range that
can be represented by nanosecond-resolution 64-bit integers."""
cdef:
bint error = False
if (dts.year <= 1677 and
cmp_npy_datetimestruct(dts, &_NS_MIN_DTS) == -1):
error = True
elif (dts.year >= 2262 and
cmp_npy_datetimestruct(dts, &_NS_MAX_DTS) == 1):
error = True
if error:
fmt = (f'{dts.year}-{dts.month:02d}-{dts.day:02d} '
f'{dts.hour:02d}:{dts.min:02d}:{dts.sec:02d}')
raise OutOfBoundsDatetime(f'Out of bounds nanosecond timestamp: {fmt}')
# ----------------------------------------------------------------------
# Conversion
cdef inline int64_t dtstruct_to_dt64(npy_datetimestruct* dts) nogil:
"""Convenience function to call npy_datetimestruct_to_datetime
with the by-far-most-common frequency NPY_FR_ns"""
return npy_datetimestruct_to_datetime(NPY_FR_ns, dts)
cdef inline void dt64_to_dtstruct(int64_t dt64,
npy_datetimestruct* out) nogil:
"""Convenience function to call pandas_datetime_to_datetimestruct
with the by-far-most-common frequency NPY_FR_ns"""
pandas_datetime_to_datetimestruct(dt64, NPY_FR_ns, out)
return
cdef inline void td64_to_tdstruct(int64_t td64,
pandas_timedeltastruct* out) nogil:
"""Convenience function to call pandas_timedelta_to_timedeltastruct
with the by-far-most-common frequency NPY_FR_ns"""
pandas_timedelta_to_timedeltastruct(td64, NPY_FR_ns, out)
return
cdef inline int64_t pydatetime_to_dt64(datetime val,
npy_datetimestruct *dts):
"""
Note we are assuming that the datetime object is timezone-naive.
"""
dts.year = PyDateTime_GET_YEAR(val)
dts.month = PyDateTime_GET_MONTH(val)
dts.day = PyDateTime_GET_DAY(val)
dts.hour = PyDateTime_DATE_GET_HOUR(val)
dts.min = PyDateTime_DATE_GET_MINUTE(val)
dts.sec = PyDateTime_DATE_GET_SECOND(val)
dts.us = PyDateTime_DATE_GET_MICROSECOND(val)
dts.ps = dts.as = 0
return dtstruct_to_dt64(dts)
cdef inline int64_t pydate_to_dt64(date val, npy_datetimestruct *dts):
dts.year = PyDateTime_GET_YEAR(val)
dts.month = PyDateTime_GET_MONTH(val)
dts.day = PyDateTime_GET_DAY(val)
dts.hour = dts.min = dts.sec = dts.us = 0
dts.ps = dts.as = 0
return dtstruct_to_dt64(dts)
cdef inline int _string_to_dts(object val, npy_datetimestruct* dts,
int* out_local, int* out_tzoffset,
bint want_exc) except? -1:
cdef:
Py_ssize_t length
const char* buf
buf = get_c_string_buf_and_size(val, &length)
return parse_iso_8601_datetime(buf, length, want_exc,
dts, out_local, out_tzoffset)