Skip to main content

pyo3_ffi/
datetime.rs

1//! FFI bindings to the functions and structs defined in `datetime.h`
2//!
3//! This is the unsafe thin  wrapper around the [CPython C API](https://docs.python.org/3/c-api/datetime.html),
4//! and covers the various date and time related objects in the Python `datetime`
5//! standard library module.
6
7#[cfg(not(PyPy))]
8use crate::PyCapsule_Import;
9#[cfg(GraalPy)]
10use crate::{PyLong_AsLong, PyLong_Check, PyObject_GetAttrString, Py_DecRef};
11use crate::{PyObject, PyObject_TypeCheck, PyTypeObject, Py_IS_TYPE, Py_None};
12use core::ffi::c_char;
13use core::ffi::c_int;
14use core::ffi::CStr;
15use core::ptr;
16use core::sync::atomic::{AtomicPtr, Ordering};
17#[cfg(not(PyPy))]
18use {crate::Py_hash_t, core::ffi::c_uchar};
19// Type struct wrappers
20const _PyDateTime_DATE_DATASIZE: usize = 4;
21const _PyDateTime_TIME_DATASIZE: usize = 6;
22const _PyDateTime_DATETIME_DATASIZE: usize = 10;
23
24#[repr(C)]
25#[derive(Debug)]
26/// Structure representing a `datetime.timedelta`.
27pub struct PyDateTime_Delta {
28    pub ob_base: PyObject,
29    #[cfg(not(PyPy))]
30    pub hashcode: Py_hash_t,
31    pub days: c_int,
32    pub seconds: c_int,
33    pub microseconds: c_int,
34}
35
36#[repr(C)]
37#[derive(Debug)]
38pub struct PyDateTime_TZInfo {
39    pub ob_base: PyObject,
40}
41
42// skipped non-limited _PyDateTime_BaseTZInfo
43
44#[cfg(not(any(PyPy, GraalPy)))]
45#[repr(C)]
46#[derive(Debug)]
47/// Structure representing a `datetime.time` without a `tzinfo` member.
48pub struct _PyDateTime_BaseTime {
49    pub ob_base: PyObject,
50    pub hashcode: Py_hash_t,
51    pub hastzinfo: c_char,
52    pub data: [c_uchar; _PyDateTime_TIME_DATASIZE],
53}
54
55#[repr(C)]
56#[derive(Debug)]
57/// Structure representing a `datetime.time`.
58pub struct PyDateTime_Time {
59    pub ob_base: PyObject,
60    #[cfg(not(PyPy))]
61    pub hashcode: Py_hash_t,
62    pub hastzinfo: c_char,
63    #[cfg(not(PyPy))]
64    pub data: [c_uchar; _PyDateTime_TIME_DATASIZE],
65    #[cfg(not(PyPy))]
66    pub fold: c_uchar,
67    /// # Safety
68    ///
69    /// Care should be taken when reading this field. If the time does not have a
70    /// tzinfo then CPython may allocate as a `_PyDateTime_BaseTime` without this field.
71    pub tzinfo: *mut PyObject,
72}
73
74#[repr(C)]
75#[derive(Debug)]
76/// Structure representing a `datetime.date`
77pub struct PyDateTime_Date {
78    pub ob_base: PyObject,
79    #[cfg(not(PyPy))]
80    pub hashcode: Py_hash_t,
81    #[cfg(not(PyPy))]
82    pub hastzinfo: c_char,
83    #[cfg(not(PyPy))]
84    pub data: [c_uchar; _PyDateTime_DATE_DATASIZE],
85}
86
87#[cfg(not(any(PyPy, GraalPy)))]
88#[repr(C)]
89#[derive(Debug)]
90/// Structure representing a `datetime.datetime` without a `tzinfo` member.
91pub struct _PyDateTime_BaseDateTime {
92    pub ob_base: PyObject,
93    pub hashcode: Py_hash_t,
94    pub hastzinfo: c_char,
95    pub data: [c_uchar; _PyDateTime_DATETIME_DATASIZE],
96}
97
98#[repr(C)]
99#[derive(Debug)]
100/// Structure representing a `datetime.datetime`.
101pub struct PyDateTime_DateTime {
102    pub ob_base: PyObject,
103    #[cfg(not(PyPy))]
104    pub hashcode: Py_hash_t,
105    pub hastzinfo: c_char,
106    #[cfg(not(PyPy))]
107    pub data: [c_uchar; _PyDateTime_DATETIME_DATASIZE],
108    #[cfg(not(PyPy))]
109    pub fold: c_uchar,
110    /// # Safety
111    ///
112    /// Care should be taken when reading this field. If the time does not have a
113    /// tzinfo then CPython may allocate as a `_PyDateTime_BaseDateTime` without this field.
114    pub tzinfo: *mut PyObject,
115}
116
117// skipped non-limited _PyDateTime_HAS_TZINFO
118
119// Accessor functions for PyDateTime_Date and PyDateTime_DateTime
120#[inline]
121#[cfg(not(any(PyPy, GraalPy)))]
122/// Retrieve the year component of a `PyDateTime_Date` or `PyDateTime_DateTime`.
123/// Returns a signed integer greater than 0.
124pub unsafe fn PyDateTime_GET_YEAR(o: *mut PyObject) -> c_int {
125    // This should work for Date or DateTime
126    let data = (*(o as *mut PyDateTime_Date)).data;
127    (c_int::from(data[0]) << 8) | c_int::from(data[1])
128}
129
130#[inline]
131#[cfg(not(any(PyPy, GraalPy)))]
132/// Retrieve the month component of a `PyDateTime_Date` or `PyDateTime_DateTime`.
133/// Returns a signed integer in the range `[1, 12]`.
134pub unsafe fn PyDateTime_GET_MONTH(o: *mut PyObject) -> c_int {
135    let data = (*(o as *mut PyDateTime_Date)).data;
136    c_int::from(data[2])
137}
138
139#[inline]
140#[cfg(not(any(PyPy, GraalPy)))]
141/// Retrieve the day component of a `PyDateTime_Date` or `PyDateTime_DateTime`.
142/// Returns a signed integer in the interval `[1, 31]`.
143pub unsafe fn PyDateTime_GET_DAY(o: *mut PyObject) -> c_int {
144    let data = (*(o as *mut PyDateTime_Date)).data;
145    c_int::from(data[3])
146}
147
148// Accessor macros for times
149#[cfg(not(any(PyPy, GraalPy)))]
150macro_rules! _PyDateTime_GET_HOUR {
151    ($o: expr, $offset:expr) => {
152        c_int::from((*$o).data[$offset + 0])
153    };
154}
155
156#[cfg(not(any(PyPy, GraalPy)))]
157macro_rules! _PyDateTime_GET_MINUTE {
158    ($o: expr, $offset:expr) => {
159        c_int::from((*$o).data[$offset + 1])
160    };
161}
162
163#[cfg(not(any(PyPy, GraalPy)))]
164macro_rules! _PyDateTime_GET_SECOND {
165    ($o: expr, $offset:expr) => {
166        c_int::from((*$o).data[$offset + 2])
167    };
168}
169
170#[cfg(not(any(PyPy, GraalPy)))]
171macro_rules! _PyDateTime_GET_MICROSECOND {
172    ($o: expr, $offset:expr) => {
173        (c_int::from((*$o).data[$offset + 3]) << 16)
174            | (c_int::from((*$o).data[$offset + 4]) << 8)
175            | (c_int::from((*$o).data[$offset + 5]))
176    };
177}
178
179#[cfg(not(any(PyPy, GraalPy)))]
180macro_rules! _PyDateTime_GET_FOLD {
181    ($o: expr) => {
182        (*$o).fold
183    };
184}
185
186#[cfg(not(any(PyPy, GraalPy)))]
187macro_rules! _PyDateTime_GET_TZINFO {
188    ($o: expr) => {
189        if (*$o).hastzinfo != 0 {
190            (*$o).tzinfo
191        } else {
192            $crate::Py_None()
193        }
194    };
195}
196
197// Accessor functions for DateTime
198#[inline]
199#[cfg(not(any(PyPy, GraalPy)))]
200/// Retrieve the hour component of a `PyDateTime_DateTime`.
201/// Returns a signed integer in the interval `[0, 23]`
202pub unsafe fn PyDateTime_DATE_GET_HOUR(o: *mut PyObject) -> c_int {
203    _PyDateTime_GET_HOUR!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE)
204}
205
206#[inline]
207#[cfg(not(any(PyPy, GraalPy)))]
208/// Retrieve the minute component of a `PyDateTime_DateTime`.
209/// Returns a signed integer in the interval `[0, 59]`
210pub unsafe fn PyDateTime_DATE_GET_MINUTE(o: *mut PyObject) -> c_int {
211    _PyDateTime_GET_MINUTE!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE)
212}
213
214#[inline]
215#[cfg(not(any(PyPy, GraalPy)))]
216/// Retrieve the second component of a `PyDateTime_DateTime`.
217/// Returns a signed integer in the interval `[0, 59]`
218pub unsafe fn PyDateTime_DATE_GET_SECOND(o: *mut PyObject) -> c_int {
219    _PyDateTime_GET_SECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE)
220}
221
222#[inline]
223#[cfg(not(any(PyPy, GraalPy)))]
224/// Retrieve the microsecond component of a `PyDateTime_DateTime`.
225/// Returns a signed integer in the interval `[0, 999999]`
226pub unsafe fn PyDateTime_DATE_GET_MICROSECOND(o: *mut PyObject) -> c_int {
227    _PyDateTime_GET_MICROSECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE)
228}
229
230#[inline]
231#[cfg(not(any(PyPy, GraalPy)))]
232/// Retrieve the fold component of a `PyDateTime_DateTime`.
233/// Returns a signed integer in the interval `[0, 1]`
234pub unsafe fn PyDateTime_DATE_GET_FOLD(o: *mut PyObject) -> c_uchar {
235    _PyDateTime_GET_FOLD!(o as *mut PyDateTime_DateTime)
236}
237
238#[inline]
239#[cfg(not(any(PyPy, GraalPy)))]
240/// Retrieve the tzinfo component of a `PyDateTime_DateTime`.
241/// Returns a pointer to a `PyObject` that should be either NULL or an instance
242/// of a `datetime.tzinfo` subclass.
243pub unsafe fn PyDateTime_DATE_GET_TZINFO(o: *mut PyObject) -> *mut PyObject {
244    _PyDateTime_GET_TZINFO!(o as *mut PyDateTime_DateTime)
245}
246
247// Accessor functions for Time
248#[inline]
249#[cfg(not(any(PyPy, GraalPy)))]
250/// Retrieve the hour component of a `PyDateTime_Time`.
251/// Returns a signed integer in the interval `[0, 23]`
252pub unsafe fn PyDateTime_TIME_GET_HOUR(o: *mut PyObject) -> c_int {
253    _PyDateTime_GET_HOUR!((o as *mut PyDateTime_Time), 0)
254}
255
256#[inline]
257#[cfg(not(any(PyPy, GraalPy)))]
258/// Retrieve the minute component of a `PyDateTime_Time`.
259/// Returns a signed integer in the interval `[0, 59]`
260pub unsafe fn PyDateTime_TIME_GET_MINUTE(o: *mut PyObject) -> c_int {
261    _PyDateTime_GET_MINUTE!((o as *mut PyDateTime_Time), 0)
262}
263
264#[inline]
265#[cfg(not(any(PyPy, GraalPy)))]
266/// Retrieve the second component of a `PyDateTime_DateTime`.
267/// Returns a signed integer in the interval `[0, 59]`
268pub unsafe fn PyDateTime_TIME_GET_SECOND(o: *mut PyObject) -> c_int {
269    _PyDateTime_GET_SECOND!((o as *mut PyDateTime_Time), 0)
270}
271
272#[inline]
273#[cfg(not(any(PyPy, GraalPy)))]
274/// Retrieve the microsecond component of a `PyDateTime_DateTime`.
275/// Returns a signed integer in the interval `[0, 999999]`
276pub unsafe fn PyDateTime_TIME_GET_MICROSECOND(o: *mut PyObject) -> c_int {
277    _PyDateTime_GET_MICROSECOND!((o as *mut PyDateTime_Time), 0)
278}
279
280#[cfg(not(any(PyPy, GraalPy)))]
281#[inline]
282/// Retrieve the fold component of a `PyDateTime_Time`.
283/// Returns a signed integer in the interval `[0, 1]`
284pub unsafe fn PyDateTime_TIME_GET_FOLD(o: *mut PyObject) -> c_uchar {
285    _PyDateTime_GET_FOLD!(o as *mut PyDateTime_Time)
286}
287
288#[inline]
289#[cfg(not(any(PyPy, GraalPy)))]
290/// Retrieve the tzinfo component of a `PyDateTime_Time`.
291/// Returns a pointer to a `PyObject` that should be either NULL or an instance
292/// of a `datetime.tzinfo` subclass.
293pub unsafe fn PyDateTime_TIME_GET_TZINFO(o: *mut PyObject) -> *mut PyObject {
294    _PyDateTime_GET_TZINFO!(o as *mut PyDateTime_Time)
295}
296
297// Accessor functions
298#[cfg(not(any(PyPy, GraalPy)))]
299macro_rules! _access_field {
300    ($obj:expr, $type: ident, $field:ident) => {
301        (*($obj as *mut $type)).$field
302    };
303}
304
305// Accessor functions for PyDateTime_Delta
306#[cfg(not(any(PyPy, GraalPy)))]
307macro_rules! _access_delta_field {
308    ($obj:expr, $field:ident) => {
309        _access_field!($obj, PyDateTime_Delta, $field)
310    };
311}
312
313#[inline]
314#[cfg(not(any(PyPy, GraalPy)))]
315/// Retrieve the days component of a `PyDateTime_Delta`.
316///
317/// Returns a signed integer in the interval [-999999999, 999999999].
318///
319/// Note: This retrieves a component from the underlying structure, it is *not*
320/// a representation of the total duration of the structure.
321pub unsafe fn PyDateTime_DELTA_GET_DAYS(o: *mut PyObject) -> c_int {
322    _access_delta_field!(o, days)
323}
324
325#[inline]
326#[cfg(not(any(PyPy, GraalPy)))]
327/// Retrieve the seconds component of a `PyDateTime_Delta`.
328///
329/// Returns a signed integer in the interval [0, 86399].
330///
331/// Note: This retrieves a component from the underlying structure, it is *not*
332/// a representation of the total duration of the structure.
333pub unsafe fn PyDateTime_DELTA_GET_SECONDS(o: *mut PyObject) -> c_int {
334    _access_delta_field!(o, seconds)
335}
336
337#[inline]
338#[cfg(not(any(PyPy, GraalPy)))]
339/// Retrieve the seconds component of a `PyDateTime_Delta`.
340///
341/// Returns a signed integer in the interval [0, 999999].
342///
343/// Note: This retrieves a component from the underlying structure, it is *not*
344/// a representation of the total duration of the structure.
345pub unsafe fn PyDateTime_DELTA_GET_MICROSECONDS(o: *mut PyObject) -> c_int {
346    _access_delta_field!(o, microseconds)
347}
348
349// Accessor functions for GraalPy. The macros on GraalPy work differently,
350// but copying them seems suboptimal
351#[inline]
352#[cfg(GraalPy)]
353pub unsafe fn _get_attr(obj: *mut PyObject, field: &core::ffi::CStr) -> c_int {
354    let result = PyObject_GetAttrString(obj, field.as_ptr());
355    Py_DecRef(result); // the original macros are borrowing
356    if PyLong_Check(result) == 1 {
357        PyLong_AsLong(result) as c_int
358    } else {
359        0
360    }
361}
362
363#[inline]
364#[cfg(GraalPy)]
365pub unsafe fn PyDateTime_GET_YEAR(o: *mut PyObject) -> c_int {
366    _get_attr(o, c"year")
367}
368
369#[inline]
370#[cfg(GraalPy)]
371pub unsafe fn PyDateTime_GET_MONTH(o: *mut PyObject) -> c_int {
372    _get_attr(o, c"month")
373}
374
375#[inline]
376#[cfg(GraalPy)]
377pub unsafe fn PyDateTime_GET_DAY(o: *mut PyObject) -> c_int {
378    _get_attr(o, c"day")
379}
380
381#[inline]
382#[cfg(GraalPy)]
383pub unsafe fn PyDateTime_DATE_GET_HOUR(o: *mut PyObject) -> c_int {
384    _get_attr(o, c"hour")
385}
386
387#[inline]
388#[cfg(GraalPy)]
389pub unsafe fn PyDateTime_DATE_GET_MINUTE(o: *mut PyObject) -> c_int {
390    _get_attr(o, c"minute")
391}
392
393#[inline]
394#[cfg(GraalPy)]
395pub unsafe fn PyDateTime_DATE_GET_SECOND(o: *mut PyObject) -> c_int {
396    _get_attr(o, c"second")
397}
398
399#[inline]
400#[cfg(GraalPy)]
401pub unsafe fn PyDateTime_DATE_GET_MICROSECOND(o: *mut PyObject) -> c_int {
402    _get_attr(o, c"microsecond")
403}
404
405#[inline]
406#[cfg(GraalPy)]
407pub unsafe fn PyDateTime_DATE_GET_FOLD(o: *mut PyObject) -> c_int {
408    _get_attr(o, c"fold")
409}
410
411#[inline]
412#[cfg(GraalPy)]
413pub unsafe fn PyDateTime_DATE_GET_TZINFO(o: *mut PyObject) -> *mut PyObject {
414    let res = PyObject_GetAttrString(o, c"tzinfo".as_ptr().cast());
415    Py_DecRef(res); // the original macros are borrowing
416    res
417}
418
419#[inline]
420#[cfg(GraalPy)]
421pub unsafe fn PyDateTime_TIME_GET_HOUR(o: *mut PyObject) -> c_int {
422    _get_attr(o, c"hour")
423}
424
425#[inline]
426#[cfg(GraalPy)]
427pub unsafe fn PyDateTime_TIME_GET_MINUTE(o: *mut PyObject) -> c_int {
428    _get_attr(o, c"minute")
429}
430
431#[inline]
432#[cfg(GraalPy)]
433pub unsafe fn PyDateTime_TIME_GET_SECOND(o: *mut PyObject) -> c_int {
434    _get_attr(o, c"second")
435}
436
437#[inline]
438#[cfg(GraalPy)]
439pub unsafe fn PyDateTime_TIME_GET_MICROSECOND(o: *mut PyObject) -> c_int {
440    _get_attr(o, c"microsecond")
441}
442
443#[inline]
444#[cfg(GraalPy)]
445pub unsafe fn PyDateTime_TIME_GET_FOLD(o: *mut PyObject) -> c_int {
446    _get_attr(o, c"fold")
447}
448
449#[inline]
450#[cfg(GraalPy)]
451pub unsafe fn PyDateTime_TIME_GET_TZINFO(o: *mut PyObject) -> *mut PyObject {
452    let res = PyObject_GetAttrString(o, c"tzinfo".as_ptr().cast());
453    Py_DecRef(res); // the original macros are borrowing
454    res
455}
456
457#[inline]
458#[cfg(GraalPy)]
459pub unsafe fn PyDateTime_DELTA_GET_DAYS(o: *mut PyObject) -> c_int {
460    _get_attr(o, c"days")
461}
462
463#[inline]
464#[cfg(GraalPy)]
465pub unsafe fn PyDateTime_DELTA_GET_SECONDS(o: *mut PyObject) -> c_int {
466    _get_attr(o, c"seconds")
467}
468
469#[inline]
470#[cfg(GraalPy)]
471pub unsafe fn PyDateTime_DELTA_GET_MICROSECONDS(o: *mut PyObject) -> c_int {
472    _get_attr(o, c"microseconds")
473}
474
475#[cfg(PyPy)]
476extern_libpython! {
477    // skipped _PyDateTime_HAS_TZINFO (not in PyPy)
478    #[link_name = "PyPyDateTime_GET_YEAR"]
479    pub fn PyDateTime_GET_YEAR(o: *mut PyObject) -> c_int;
480    #[link_name = "PyPyDateTime_GET_MONTH"]
481    pub fn PyDateTime_GET_MONTH(o: *mut PyObject) -> c_int;
482    #[link_name = "PyPyDateTime_GET_DAY"]
483    pub fn PyDateTime_GET_DAY(o: *mut PyObject) -> c_int;
484
485    #[link_name = "PyPyDateTime_DATE_GET_HOUR"]
486    pub fn PyDateTime_DATE_GET_HOUR(o: *mut PyObject) -> c_int;
487    #[link_name = "PyPyDateTime_DATE_GET_MINUTE"]
488    pub fn PyDateTime_DATE_GET_MINUTE(o: *mut PyObject) -> c_int;
489    #[link_name = "PyPyDateTime_DATE_GET_SECOND"]
490    pub fn PyDateTime_DATE_GET_SECOND(o: *mut PyObject) -> c_int;
491    #[link_name = "PyPyDateTime_DATE_GET_MICROSECOND"]
492    pub fn PyDateTime_DATE_GET_MICROSECOND(o: *mut PyObject) -> c_int;
493    #[link_name = "PyPyDateTime_GET_FOLD"]
494    pub fn PyDateTime_DATE_GET_FOLD(o: *mut PyObject) -> c_int;
495    #[link_name = "PyPyDateTime_DATE_GET_TZINFO"]
496    #[cfg(Py_3_10)]
497    pub fn PyDateTime_DATE_GET_TZINFO(o: *mut PyObject) -> *mut PyObject;
498
499    #[link_name = "PyPyDateTime_TIME_GET_HOUR"]
500    pub fn PyDateTime_TIME_GET_HOUR(o: *mut PyObject) -> c_int;
501    #[link_name = "PyPyDateTime_TIME_GET_MINUTE"]
502    pub fn PyDateTime_TIME_GET_MINUTE(o: *mut PyObject) -> c_int;
503    #[link_name = "PyPyDateTime_TIME_GET_SECOND"]
504    pub fn PyDateTime_TIME_GET_SECOND(o: *mut PyObject) -> c_int;
505    #[link_name = "PyPyDateTime_TIME_GET_MICROSECOND"]
506    pub fn PyDateTime_TIME_GET_MICROSECOND(o: *mut PyObject) -> c_int;
507    #[link_name = "PyPyDateTime_TIME_GET_FOLD"]
508    pub fn PyDateTime_TIME_GET_FOLD(o: *mut PyObject) -> c_int;
509    #[link_name = "PyPyDateTime_TIME_GET_TZINFO"]
510    #[cfg(Py_3_10)]
511    pub fn PyDateTime_TIME_GET_TZINFO(o: *mut PyObject) -> *mut PyObject;
512
513    #[link_name = "PyPyDateTime_DELTA_GET_DAYS"]
514    pub fn PyDateTime_DELTA_GET_DAYS(o: *mut PyObject) -> c_int;
515    #[link_name = "PyPyDateTime_DELTA_GET_SECONDS"]
516    pub fn PyDateTime_DELTA_GET_SECONDS(o: *mut PyObject) -> c_int;
517    #[link_name = "PyPyDateTime_DELTA_GET_MICROSECONDS"]
518    pub fn PyDateTime_DELTA_GET_MICROSECONDS(o: *mut PyObject) -> c_int;
519}
520
521#[repr(C)]
522#[derive(Debug, Copy, Clone)]
523pub struct PyDateTime_CAPI {
524    pub DateType: *mut PyTypeObject,
525    pub DateTimeType: *mut PyTypeObject,
526    pub TimeType: *mut PyTypeObject,
527    pub DeltaType: *mut PyTypeObject,
528    pub TZInfoType: *mut PyTypeObject,
529    pub TimeZone_UTC: *mut PyObject,
530    pub Date_FromDate: unsafe extern "C" fn(
531        year: c_int,
532        month: c_int,
533        day: c_int,
534        cls: *mut PyTypeObject,
535    ) -> *mut PyObject,
536    pub DateTime_FromDateAndTime: unsafe extern "C" fn(
537        year: c_int,
538        month: c_int,
539        day: c_int,
540        hour: c_int,
541        minute: c_int,
542        second: c_int,
543        microsecond: c_int,
544        tzinfo: *mut PyObject,
545        cls: *mut PyTypeObject,
546    ) -> *mut PyObject,
547    pub Time_FromTime: unsafe extern "C" fn(
548        hour: c_int,
549        minute: c_int,
550        second: c_int,
551        microsecond: c_int,
552        tzinfo: *mut PyObject,
553        cls: *mut PyTypeObject,
554    ) -> *mut PyObject,
555    pub Delta_FromDelta: unsafe extern "C" fn(
556        days: c_int,
557        seconds: c_int,
558        microseconds: c_int,
559        normalize: c_int,
560        cls: *mut PyTypeObject,
561    ) -> *mut PyObject,
562    pub TimeZone_FromTimeZone:
563        unsafe extern "C" fn(offset: *mut PyObject, name: *mut PyObject) -> *mut PyObject,
564
565    pub DateTime_FromTimestamp: unsafe extern "C" fn(
566        cls: *mut PyTypeObject,
567        args: *mut PyObject,
568        kwargs: *mut PyObject,
569    ) -> *mut PyObject,
570    pub Date_FromTimestamp:
571        unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject) -> *mut PyObject,
572    pub DateTime_FromDateAndTimeAndFold: unsafe extern "C" fn(
573        year: c_int,
574        month: c_int,
575        day: c_int,
576        hour: c_int,
577        minute: c_int,
578        second: c_int,
579        microsecond: c_int,
580        tzinfo: *mut PyObject,
581        fold: c_int,
582        cls: *mut PyTypeObject,
583    ) -> *mut PyObject,
584    pub Time_FromTimeAndFold: unsafe extern "C" fn(
585        hour: c_int,
586        minute: c_int,
587        second: c_int,
588        microsecond: c_int,
589        tzinfo: *mut PyObject,
590        fold: c_int,
591        cls: *mut PyTypeObject,
592    ) -> *mut PyObject,
593}
594
595// SAFETY: Python already shares this object between threads, so it's no more evil for us to do it too!
596unsafe impl Sync for PyDateTime_CAPI {}
597
598pub const PyDateTime_CAPSULE_NAME: &CStr = c"datetime.datetime_CAPI";
599
600/// Returns a pointer to a `PyDateTime_CAPI` instance
601///
602/// # Note
603/// This function will return a null pointer until
604/// `PyDateTime_IMPORT` is called
605#[inline]
606pub unsafe fn PyDateTimeAPI() -> *mut PyDateTime_CAPI {
607    PyDateTimeAPI_impl.load(Ordering::Acquire)
608}
609
610/// Populates the `PyDateTimeAPI` object
611pub unsafe fn PyDateTime_IMPORT() {
612    if PyDateTimeAPI_impl.load(Ordering::Relaxed).is_null() {
613        // PyPy expects the C-API to be initialized via PyDateTime_Import, so trying to use
614        // `PyCapsule_Import` will behave unexpectedly in pypy.
615        #[cfg(PyPy)]
616        let py_datetime_c_api = PyDateTime_Import();
617
618        #[cfg(not(PyPy))]
619        let py_datetime_c_api =
620            PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 1) as *mut PyDateTime_CAPI;
621
622        if py_datetime_c_api.is_null() {
623            return;
624        }
625
626        // Protect against race conditions when the datetime API is concurrently
627        // initialized in multiple threads.
628        let _ = PyDateTimeAPI_impl.compare_exchange(
629            ptr::null_mut(),
630            py_datetime_c_api,
631            Ordering::Release,
632            Ordering::Relaxed,
633        );
634    }
635}
636
637#[inline]
638pub unsafe fn PyDateTime_TimeZone_UTC() -> *mut PyObject {
639    (*PyDateTimeAPI()).TimeZone_UTC
640}
641
642/// Type Check macros
643///
644/// These are bindings around the C API typecheck macros, all of them return
645/// `1` if True and `0` if False. In all type check macros, the argument (`op`)
646/// must not be `NULL`.
647#[inline]
648/// Check if `op` is a `PyDateTimeAPI.DateType` or subtype.
649pub unsafe fn PyDate_Check(op: *mut PyObject) -> c_int {
650    PyObject_TypeCheck(op, (*PyDateTimeAPI()).DateType) as c_int
651}
652
653#[inline]
654/// Check if `op`'s type is exactly `PyDateTimeAPI.DateType`.
655pub unsafe fn PyDate_CheckExact(op: *mut PyObject) -> c_int {
656    Py_IS_TYPE(op, (*PyDateTimeAPI()).DateType)
657}
658
659#[inline]
660/// Check if `op` is a `PyDateTimeAPI.DateTimeType` or subtype.
661pub unsafe fn PyDateTime_Check(op: *mut PyObject) -> c_int {
662    PyObject_TypeCheck(op, (*PyDateTimeAPI()).DateTimeType) as c_int
663}
664
665#[inline]
666/// Check if `op`'s type is exactly `PyDateTimeAPI.DateTimeType`.
667pub unsafe fn PyDateTime_CheckExact(op: *mut PyObject) -> c_int {
668    Py_IS_TYPE(op, (*PyDateTimeAPI()).DateTimeType)
669}
670
671#[inline]
672/// Check if `op` is a `PyDateTimeAPI.TimeType` or subtype.
673pub unsafe fn PyTime_Check(op: *mut PyObject) -> c_int {
674    PyObject_TypeCheck(op, (*PyDateTimeAPI()).TimeType) as c_int
675}
676
677#[inline]
678/// Check if `op`'s type is exactly `PyDateTimeAPI.TimeType`.
679pub unsafe fn PyTime_CheckExact(op: *mut PyObject) -> c_int {
680    Py_IS_TYPE(op, (*PyDateTimeAPI()).TimeType)
681}
682
683#[inline]
684/// Check if `op` is a `PyDateTimeAPI.DetaType` or subtype.
685pub unsafe fn PyDelta_Check(op: *mut PyObject) -> c_int {
686    PyObject_TypeCheck(op, (*PyDateTimeAPI()).DeltaType) as c_int
687}
688
689#[inline]
690/// Check if `op`'s type is exactly `PyDateTimeAPI.DeltaType`.
691pub unsafe fn PyDelta_CheckExact(op: *mut PyObject) -> c_int {
692    Py_IS_TYPE(op, (*PyDateTimeAPI()).DeltaType)
693}
694
695#[inline]
696/// Check if `op` is a `PyDateTimeAPI.TZInfoType` or subtype.
697pub unsafe fn PyTZInfo_Check(op: *mut PyObject) -> c_int {
698    PyObject_TypeCheck(op, (*PyDateTimeAPI()).TZInfoType) as c_int
699}
700
701#[inline]
702/// Check if `op`'s type is exactly `PyDateTimeAPI.TZInfoType`.
703pub unsafe fn PyTZInfo_CheckExact(op: *mut PyObject) -> c_int {
704    Py_IS_TYPE(op, (*PyDateTimeAPI()).TZInfoType)
705}
706
707pub unsafe fn PyDate_FromDate(year: c_int, month: c_int, day: c_int) -> *mut PyObject {
708    ((*PyDateTimeAPI()).Date_FromDate)(year, month, day, (*PyDateTimeAPI()).DateType)
709}
710
711#[allow(clippy::too_many_arguments)]
712/// See <https://github.com/python/cpython/blob/3.10/Include/datetime.h#L226-L228>
713pub unsafe fn PyDateTime_FromDateAndTime(
714    year: c_int,
715    month: c_int,
716    day: c_int,
717    hour: c_int,
718    minute: c_int,
719    second: c_int,
720    microsecond: c_int,
721) -> *mut PyObject {
722    ((*PyDateTimeAPI()).DateTime_FromDateAndTime)(
723        year,
724        month,
725        day,
726        hour,
727        minute,
728        second,
729        microsecond,
730        Py_None(),
731        (*PyDateTimeAPI()).DateTimeType,
732    )
733}
734
735#[allow(clippy::too_many_arguments)]
736/// See <https://github.com/python/cpython/blob/3.10/Include/datetime.h#L230-L232>
737pub unsafe fn PyDateTime_FromDateAndTimeAndFold(
738    year: c_int,
739    month: c_int,
740    day: c_int,
741    hour: c_int,
742    minute: c_int,
743    second: c_int,
744    microsecond: c_int,
745    fold: c_int,
746) -> *mut PyObject {
747    ((*PyDateTimeAPI()).DateTime_FromDateAndTimeAndFold)(
748        year,
749        month,
750        day,
751        hour,
752        minute,
753        second,
754        microsecond,
755        Py_None(),
756        fold,
757        (*PyDateTimeAPI()).DateTimeType,
758    )
759}
760
761pub unsafe fn PyTime_FromTime(
762    hour: c_int,
763    minute: c_int,
764    second: c_int,
765    microsecond: c_int,
766) -> *mut PyObject {
767    ((*PyDateTimeAPI()).Time_FromTime)(
768        hour,
769        minute,
770        second,
771        microsecond,
772        Py_None(),
773        (*PyDateTimeAPI()).TimeType,
774    )
775}
776
777pub unsafe fn PyTime_FromTimeAndFold(
778    hour: c_int,
779    minute: c_int,
780    second: c_int,
781    microsecond: c_int,
782    fold: c_int,
783) -> *mut PyObject {
784    ((*PyDateTimeAPI()).Time_FromTimeAndFold)(
785        hour,
786        minute,
787        second,
788        microsecond,
789        Py_None(),
790        fold,
791        (*PyDateTimeAPI()).TimeType,
792    )
793}
794
795pub unsafe fn PyDelta_FromDSU(days: c_int, seconds: c_int, microseconds: c_int) -> *mut PyObject {
796    ((*PyDateTimeAPI()).Delta_FromDelta)(
797        days,
798        seconds,
799        microseconds,
800        1,
801        (*PyDateTimeAPI()).DeltaType,
802    )
803}
804
805pub unsafe fn PyTimeZone_FromOffset(offset: *mut PyObject) -> *mut PyObject {
806    ((*PyDateTimeAPI()).TimeZone_FromTimeZone)(offset, core::ptr::null_mut())
807}
808
809pub unsafe fn PyTimeZone_FromOffsetAndName(
810    offset: *mut PyObject,
811    name: *mut PyObject,
812) -> *mut PyObject {
813    ((*PyDateTimeAPI()).TimeZone_FromTimeZone)(offset, name)
814}
815
816#[cfg(not(PyPy))]
817pub unsafe fn PyDateTime_FromTimestamp(args: *mut PyObject) -> *mut PyObject {
818    let f = (*PyDateTimeAPI()).DateTime_FromTimestamp;
819    f((*PyDateTimeAPI()).DateTimeType, args, core::ptr::null_mut())
820}
821
822#[cfg(not(PyPy))]
823pub unsafe fn PyDate_FromTimestamp(args: *mut PyObject) -> *mut PyObject {
824    let f = (*PyDateTimeAPI()).Date_FromTimestamp;
825    f((*PyDateTimeAPI()).DateType, args)
826}
827
828#[cfg(PyPy)]
829extern_libpython! {
830    #[link_name = "PyPyDate_FromTimestamp"]
831    pub fn PyDate_FromTimestamp(args: *mut PyObject) -> *mut PyObject;
832    #[link_name = "PyPyDateTime_FromTimestamp"]
833    pub fn PyDateTime_FromTimestamp(args: *mut PyObject) -> *mut PyObject;
834}
835
836#[cfg(PyPy)]
837extern_libpython! {
838    #[link_name = "_PyPyDateTime_Import"]
839    pub fn PyDateTime_Import() -> *mut PyDateTime_CAPI;
840}
841
842// Rust specific implementation details
843
844static PyDateTimeAPI_impl: AtomicPtr<PyDateTime_CAPI> = AtomicPtr::new(ptr::null_mut());
⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here