File size: 15,793 Bytes
64772a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
from cpython.object cimport PyObject
from cpython.version cimport PY_VERSION_HEX

cdef extern from "Python.h":
    ctypedef struct PyTypeObject:
        pass

cdef extern from "datetime.h":
    """
    /* Backport for Python 2.x */
    #if PY_MAJOR_VERSION < 3
        #ifndef PyDateTime_DELTA_GET_DAYS
            #define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days)
        #endif
        #ifndef PyDateTime_DELTA_GET_SECONDS
            #define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds)
        #endif
        #ifndef PyDateTime_DELTA_GET_MICROSECONDS
            #define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds)
        #endif
    #endif

    /* Backport for Python < 3.6 */
    #if PY_VERSION_HEX < 0x030600a4
        #ifndef PyDateTime_TIME_GET_FOLD
            #define PyDateTime_TIME_GET_FOLD(o) ((void)(o), 0)
        #endif
        #ifndef PyDateTime_DATE_GET_FOLD
            #define PyDateTime_DATE_GET_FOLD(o) ((void)(o), 0)
        #endif
    #endif

    /* Backport for Python < 3.6 */
    #if PY_VERSION_HEX < 0x030600a4
        #define __Pyx_DateTime_DateTimeWithFold(year, month, day, hour, minute, second, microsecond, tz, fold) \
            ((void)(fold), PyDateTimeAPI->DateTime_FromDateAndTime(year, month, day, hour, minute, second, \
                microsecond, tz, PyDateTimeAPI->DateTimeType))
        #define __Pyx_DateTime_TimeWithFold(hour, minute, second, microsecond, tz, fold) \
            ((void)(fold), PyDateTimeAPI->Time_FromTime(hour, minute, second, microsecond, tz, PyDateTimeAPI->TimeType))
    #else /* For Python 3.6+ so that we can pass tz */
        #define __Pyx_DateTime_DateTimeWithFold(year, month, day, hour, minute, second, microsecond, tz, fold) \
            PyDateTimeAPI->DateTime_FromDateAndTimeAndFold(year, month, day, hour, minute, second, \
                microsecond, tz, fold, PyDateTimeAPI->DateTimeType)
        #define __Pyx_DateTime_TimeWithFold(hour, minute, second, microsecond, tz, fold) \
            PyDateTimeAPI->Time_FromTimeAndFold(hour, minute, second, microsecond, tz, fold, PyDateTimeAPI->TimeType)
    #endif

    /* Backport for Python < 3.7 */
    #if PY_VERSION_HEX < 0x030700b1
        #define __Pyx_TimeZone_UTC NULL
        #define __Pyx_TimeZone_FromOffsetAndName(offset, name) ((void)(offset), (void)(name), (PyObject*)NULL)
    #else
        #define __Pyx_TimeZone_UTC PyDateTime_TimeZone_UTC
        #define __Pyx_TimeZone_FromOffsetAndName(offset, name) PyTimeZone_FromOffsetAndName(offset, name)
    #endif

    /* Backport for Python < 3.10 */
    #if PY_VERSION_HEX < 0x030a00a1
        #ifndef PyDateTime_TIME_GET_TZINFO
            #define PyDateTime_TIME_GET_TZINFO(o) \
                ((((PyDateTime_Time*)o)->hastzinfo) ? ((PyDateTime_Time*)o)->tzinfo : Py_None)
        #endif
        #ifndef PyDateTime_DATE_GET_TZINFO
            #define PyDateTime_DATE_GET_TZINFO(o) \
                ((((PyDateTime_DateTime*)o)->hastzinfo) ? ((PyDateTime_DateTime*)o)->tzinfo : Py_None)
        #endif
    #endif
    """

    ctypedef extern class datetime.date[object PyDateTime_Date]:
        @property
        cdef inline int year(self) noexcept:
            return PyDateTime_GET_YEAR(self)

        @property
        cdef inline int month(self) noexcept:
            return PyDateTime_GET_MONTH(self)

        @property
        cdef inline int day(self) noexcept:
            return PyDateTime_GET_DAY(self)

    ctypedef extern class datetime.time[object PyDateTime_Time]:
        @property
        cdef inline int hour(self) noexcept:
            return PyDateTime_TIME_GET_HOUR(self)

        @property
        cdef inline int minute(self) noexcept:
            return PyDateTime_TIME_GET_MINUTE(self)

        @property
        cdef inline int second(self) noexcept:
            return PyDateTime_TIME_GET_SECOND(self)

        @property
        cdef inline int microsecond(self) noexcept:
            return PyDateTime_TIME_GET_MICROSECOND(self)

        @property
        cdef inline object tzinfo(self):
            return <object>PyDateTime_TIME_GET_TZINFO(self)

        @property
        cdef inline int fold(self) noexcept:
            # For Python < 3.6 this returns 0 no matter what
            return PyDateTime_TIME_GET_FOLD(self)

    ctypedef extern class datetime.datetime[object PyDateTime_DateTime]:
        @property
        cdef inline int year(self) noexcept:
            return PyDateTime_GET_YEAR(self)

        @property
        cdef inline int month(self) noexcept:
            return PyDateTime_GET_MONTH(self)

        @property
        cdef inline int day(self) noexcept:
            return PyDateTime_GET_DAY(self)

        @property
        cdef inline int hour(self) noexcept:
            return PyDateTime_DATE_GET_HOUR(self)

        @property
        cdef inline int minute(self) noexcept:
            return PyDateTime_DATE_GET_MINUTE(self)

        @property
        cdef inline int second(self) noexcept:
            return PyDateTime_DATE_GET_SECOND(self)

        @property
        cdef inline int microsecond(self) noexcept:
            return PyDateTime_DATE_GET_MICROSECOND(self)

        @property
        cdef inline object tzinfo(self):
            return <object>PyDateTime_DATE_GET_TZINFO(self)

        @property
        cdef inline int fold(self) noexcept:
            # For Python < 3.6 this returns 0 no matter what
            return PyDateTime_DATE_GET_FOLD(self)

    ctypedef extern class datetime.timedelta[object PyDateTime_Delta]:
        @property
        cdef inline int day(self) noexcept:
            return PyDateTime_DELTA_GET_DAYS(self)

        @property
        cdef inline int second(self) noexcept:
            return PyDateTime_DELTA_GET_SECONDS(self)

        @property
        cdef inline int microsecond(self) noexcept:
            return PyDateTime_DELTA_GET_MICROSECONDS(self)

    ctypedef extern class datetime.tzinfo[object PyDateTime_TZInfo]:
        pass

    ctypedef struct PyDateTime_Date:
        pass

    ctypedef struct PyDateTime_Time:
        unsigned char fold
        char hastzinfo
        PyObject *tzinfo

    ctypedef struct PyDateTime_DateTime:
        unsigned char fold
        char hastzinfo
        PyObject *tzinfo

    ctypedef struct PyDateTime_Delta:
        int days
        int seconds
        int microseconds

    # Define structure for C API.
    ctypedef struct PyDateTime_CAPI:
        # type objects
        PyTypeObject *DateType
        PyTypeObject *DateTimeType
        PyTypeObject *TimeType
        PyTypeObject *DeltaType
        PyTypeObject *TZInfoType

        # constructors
        date (*Date_FromDate)(int, int, int, PyTypeObject*)
        datetime (*DateTime_FromDateAndTime)(int, int, int, int, int, int, int, object, PyTypeObject*)
        time (*Time_FromTime)(int, int, int, int, object, PyTypeObject*)
        timedelta (*Delta_FromDelta)(int, int, int, int, PyTypeObject*)

        # constructors for the DB API
        datetime (*DateTime_FromTimestamp)(PyObject*, object, PyObject*)
        date (*Date_FromTimestamp)(PyObject*, object)

        # We cannot use the following because they do not compile in older Python versions.
        # Instead, we use datetime.h's macros here that we can backport in C.

        # Python 3.7+ constructors
        object (*TimeZone_FromTimeZone)(object offset, PyObject *name)

        # Python 3.7+ singletons
        PyObject *TimeZone_UTC

        # Python 3.6+ PEP 495 constructors
        datetime (*DateTime_FromDateAndTimeAndFold)(int, int, int, int, int, int, int, object, int, PyTypeObject*)
        time (*Time_FromTimeAndFold)(int, int, int ,int, object, int, PyTypeObject*)

    # Check type of the object.
    bint PyDate_Check(object op)
    bint PyDate_CheckExact(object op)

    bint PyDateTime_Check(object op)
    bint PyDateTime_CheckExact(object op)

    bint PyTime_Check(object op)
    bint PyTime_CheckExact(object op)

    bint PyDelta_Check(object op)
    bint PyDelta_CheckExact(object op)

    bint PyTZInfo_Check(object op)
    bint PyTZInfo_CheckExact(object op)

    # Getters for date and datetime (C macros).
    int PyDateTime_GET_YEAR(object o)
    int PyDateTime_GET_MONTH(object o)
    int PyDateTime_GET_DAY(object o)

    # Getters for datetime (C macros).
    int PyDateTime_DATE_GET_HOUR(object o)
    int PyDateTime_DATE_GET_MINUTE(object o)
    int PyDateTime_DATE_GET_SECOND(object o)
    int PyDateTime_DATE_GET_MICROSECOND(object o)
    int PyDateTime_DATE_GET_FOLD(object o)
    PyObject* PyDateTime_DATE_GET_TZINFO(object o)  # returns a borrowed reference

    # Getters for time (C macros).
    int PyDateTime_TIME_GET_HOUR(object o)
    int PyDateTime_TIME_GET_MINUTE(object o)
    int PyDateTime_TIME_GET_SECOND(object o)
    int PyDateTime_TIME_GET_MICROSECOND(object o)
    int PyDateTime_TIME_GET_FOLD(object o)
    PyObject* PyDateTime_TIME_GET_TZINFO(object o)  # returns a borrowed reference

    # Getters for timedelta (C macros).
    int PyDateTime_DELTA_GET_DAYS(object o)
    int PyDateTime_DELTA_GET_SECONDS(object o)
    int PyDateTime_DELTA_GET_MICROSECONDS(object o)

    # Constructors
    object PyTimeZone_FromOffset(object offset)
    object PyTimeZone_FromOffsetAndName(object offset, object name)

    # The above macros is Python 3.7+ so we use these instead
    object __Pyx_TimeZone_FromOffsetAndName(object offset, PyObject* name)

    # Constructors for the DB API
    datetime PyDateTime_FromTimeStamp(object args)
    date PyDate_FromTimeStamp(object args)

    # PEP 495 constructors but patched above to allow passing tz
    datetime __Pyx_DateTime_DateTimeWithFold(int, int, int, int, int, int, int, object, int)
    datetime __Pyx_DateTime_TimeWithFold(int, int, int ,int, object, int)

    # PyDateTime CAPI object.
    PyDateTime_CAPI *PyDateTimeAPI

    PyObject* PyDateTime_TimeZone_UTC

    # PyDateTime_TimeZone_UTC is Python 3.7+ so instead we use the following macro
    PyObject* __Pyx_TimeZone_UTC

    void PyDateTime_IMPORT()

# Datetime C API initialization function.
# You have to call it before any usage of DateTime CAPI functions.
cdef inline void import_datetime() noexcept:
    PyDateTime_IMPORT

# Create date object using DateTime CAPI factory function.
# Note, there are no range checks for any of the arguments.
cdef inline date date_new(int year, int month, int day):
    return PyDateTimeAPI.Date_FromDate(year, month, day, PyDateTimeAPI.DateType)

# Create time object using DateTime CAPI factory function
# Note, there are no range checks for any of the arguments.
cdef inline time time_new(int hour, int minute, int second, int microsecond, object tz, int fold=0):
    return __Pyx_DateTime_TimeWithFold(hour, minute, second, microsecond, tz, fold)

# Create datetime object using DateTime CAPI factory function.
# Note, there are no range checks for any of the arguments.
cdef inline datetime datetime_new(int year, int month, int day, int hour, int minute, int second, int microsecond, object tz, int fold=0):
    return __Pyx_DateTime_DateTimeWithFold(year, month, day, hour, minute, second, microsecond, tz, fold)

# Create timedelta object using DateTime CAPI factory function.
# Note, there are no range checks for any of the arguments.
cdef inline timedelta timedelta_new(int days, int seconds, int useconds):
    return PyDateTimeAPI.Delta_FromDelta(days, seconds, useconds, 1, PyDateTimeAPI.DeltaType)

# Create timedelta object using DateTime CAPI factory function.
cdef inline object timezone_new(object offset, object name=None):
    if PY_VERSION_HEX < 0x030700b1:
        raise RuntimeError('Time zones are not available from the C-API.')
    return __Pyx_TimeZone_FromOffsetAndName(offset, <PyObject*>name if name is not None else NULL)

# Create datetime object using DB API constructor.
cdef inline datetime datetime_from_timestamp(timestamp, tz=None):
    return PyDateTimeAPI.DateTime_FromTimestamp(
        <PyObject*>PyDateTimeAPI.DateTimeType, (timestamp, tz) if tz is not None else (timestamp,), NULL)

# Create date object using DB API constructor.
cdef inline date date_from_timestamp(timestamp):
    return PyDateTimeAPI.Date_FromTimestamp(<PyObject*>PyDateTimeAPI.DateType, (timestamp,))

# More recognizable getters for date/time/datetime/timedelta.
# There are no setters because datetime.h hasn't them.
# This is because of immutable nature of these objects by design.
# If you would change time/date/datetime/timedelta object you need to recreate.

# Get UTC singleton
cdef inline object get_utc():
    if PY_VERSION_HEX < 0x030700b1:
        raise RuntimeError('Time zones are not available from the C-API.')
    return <object>__Pyx_TimeZone_UTC

# Get tzinfo of time
cdef inline object time_tzinfo(object o):
    return <object>PyDateTime_TIME_GET_TZINFO(o)

# Get tzinfo of datetime
cdef inline object datetime_tzinfo(object o):
    return <object>PyDateTime_DATE_GET_TZINFO(o)

# Get year of date
cdef inline int date_year(object o) noexcept:
    return PyDateTime_GET_YEAR(o)

# Get month of date
cdef inline int date_month(object o) noexcept:
    return PyDateTime_GET_MONTH(o)

# Get day of date
cdef inline int date_day(object o) noexcept:
    return PyDateTime_GET_DAY(o)

# Get year of datetime
cdef inline int datetime_year(object o) noexcept:
    return PyDateTime_GET_YEAR(o)

# Get month of datetime
cdef inline int datetime_month(object o) noexcept:
    return PyDateTime_GET_MONTH(o)

# Get day of datetime
cdef inline int datetime_day(object o) noexcept:
    return PyDateTime_GET_DAY(o)

# Get hour of time
cdef inline int time_hour(object o) noexcept:
    return PyDateTime_TIME_GET_HOUR(o)

# Get minute of time
cdef inline int time_minute(object o) noexcept:
    return PyDateTime_TIME_GET_MINUTE(o)

# Get second of time
cdef inline int time_second(object o) noexcept:
    return PyDateTime_TIME_GET_SECOND(o)

# Get microsecond of time
cdef inline int time_microsecond(object o) noexcept:
    return PyDateTime_TIME_GET_MICROSECOND(o)

# Get fold of time
cdef inline int time_fold(object o) noexcept:
    # For Python < 3.6 this returns 0 no matter what
    return PyDateTime_TIME_GET_FOLD(o)

# Get hour of datetime
cdef inline int datetime_hour(object o) noexcept:
    return PyDateTime_DATE_GET_HOUR(o)

# Get minute of datetime
cdef inline int datetime_minute(object o) noexcept:
    return PyDateTime_DATE_GET_MINUTE(o)

# Get second of datetime
cdef inline int datetime_second(object o) noexcept:
    return PyDateTime_DATE_GET_SECOND(o)

# Get microsecond of datetime
cdef inline int datetime_microsecond(object o) noexcept:
    return PyDateTime_DATE_GET_MICROSECOND(o)

# Get fold of datetime
cdef inline int datetime_fold(object o) noexcept:
    # For Python < 3.6 this returns 0 no matter what
    return PyDateTime_DATE_GET_FOLD(o)

# Get days of timedelta
cdef inline int timedelta_days(object o) noexcept:
    return (<PyDateTime_Delta*>o).days

# Get seconds of timedelta
cdef inline int timedelta_seconds(object o) noexcept:
    return (<PyDateTime_Delta*>o).seconds

# Get microseconds of timedelta
cdef inline int timedelta_microseconds(object o) noexcept:
    return (<PyDateTime_Delta*>o).microseconds

cdef inline double total_seconds(timedelta obj) noexcept:
    # Mirrors the "timedelta.total_seconds()" method.
    # Note that this implementation is not guaranteed to give *exactly* the same
    # result as the original method, due to potential differences in floating point rounding.
    cdef:
        double days, seconds, micros
    days = <double>PyDateTime_DELTA_GET_DAYS(obj)
    seconds = <double>PyDateTime_DELTA_GET_SECONDS(obj)
    micros = <double>PyDateTime_DELTA_GET_MICROSECONDS(obj)
    return days * 24 * 3600 + seconds + micros / 1_000_000