Skip to main content

pyforge_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
7use crate::PyCapsule_Import;
8use crate::{PyObject, PyObject_TypeCheck, PyTypeObject, Py_TYPE};
9use std::ffi::c_char;
10use std::ffi::c_int;
11use std::ptr;
12use std::sync::Once;
13use std::{cell::UnsafeCell, ffi::CStr};
14use {crate::Py_hash_t, std::ffi::c_uchar};
15// Type struct wrappers
16const _PyDateTime_DATE_DATASIZE: usize = 4;
17const _PyDateTime_TIME_DATASIZE: usize = 6;
18const _PyDateTime_DATETIME_DATASIZE: usize = 10;
19
20#[repr(C)]
21#[derive(Debug)]
22/// Structure representing a `datetime.timedelta`.
23pub struct PyDateTime_Delta {
24    pub ob_base: PyObject,
25    pub hashcode: Py_hash_t,
26    pub days: c_int,
27    pub seconds: c_int,
28    pub microseconds: c_int,
29}
30
31// skipped non-limited PyDateTime_TZInfo
32// skipped non-limited _PyDateTime_BaseTZInfo
33
34#[repr(C)]
35#[derive(Debug)]
36/// Structure representing a `datetime.time` without a `tzinfo` member.
37pub struct _PyDateTime_BaseTime {
38    pub ob_base: PyObject,
39    pub hashcode: Py_hash_t,
40    pub hastzinfo: c_char,
41    pub data: [c_uchar; _PyDateTime_TIME_DATASIZE],
42}
43
44#[repr(C)]
45#[derive(Debug)]
46/// Structure representing a `datetime.time`.
47pub struct PyDateTime_Time {
48    pub ob_base: PyObject,
49    pub hashcode: Py_hash_t,
50    pub hastzinfo: c_char,
51    pub data: [c_uchar; _PyDateTime_TIME_DATASIZE],
52    pub fold: c_uchar,
53    /// # Safety
54    ///
55    /// Care should be taken when reading this field. If the time does not have a
56    /// tzinfo then CPython may allocate as a `_PyDateTime_BaseTime` without this field.
57    pub tzinfo: *mut PyObject,
58}
59
60#[repr(C)]
61#[derive(Debug)]
62/// Structure representing a `datetime.date`
63pub struct PyDateTime_Date {
64    pub ob_base: PyObject,
65    pub hashcode: Py_hash_t,
66    pub hastzinfo: c_char,
67    pub data: [c_uchar; _PyDateTime_DATE_DATASIZE],
68}
69
70#[repr(C)]
71#[derive(Debug)]
72/// Structure representing a `datetime.datetime` without a `tzinfo` member.
73pub struct _PyDateTime_BaseDateTime {
74    pub ob_base: PyObject,
75    pub hashcode: Py_hash_t,
76    pub hastzinfo: c_char,
77    pub data: [c_uchar; _PyDateTime_DATETIME_DATASIZE],
78}
79
80#[repr(C)]
81#[derive(Debug)]
82/// Structure representing a `datetime.datetime`.
83pub struct PyDateTime_DateTime {
84    pub ob_base: PyObject,
85    pub hashcode: Py_hash_t,
86    pub hastzinfo: c_char,
87    pub data: [c_uchar; _PyDateTime_DATETIME_DATASIZE],
88    pub fold: c_uchar,
89    /// # Safety
90    ///
91    /// Care should be taken when reading this field. If the time does not have a
92    /// tzinfo then CPython may allocate as a `_PyDateTime_BaseDateTime` without this field.
93    pub tzinfo: *mut PyObject,
94}
95
96// skipped non-limited _PyDateTime_HAS_TZINFO
97
98// Accessor functions for PyDateTime_Date and PyDateTime_DateTime
99#[inline]
100/// Retrieve the year component of a `PyDateTime_Date` or `PyDateTime_DateTime`.
101/// Returns a signed integer greater than 0.
102pub unsafe fn PyDateTime_GET_YEAR(o: *mut PyObject) -> c_int {
103    // This should work for Date or DateTime
104    let data = (*(o as *mut PyDateTime_Date)).data;
105    (c_int::from(data[0]) << 8) | c_int::from(data[1])
106}
107
108#[inline]
109/// Retrieve the month component of a `PyDateTime_Date` or `PyDateTime_DateTime`.
110/// Returns a signed integer in the range `[1, 12]`.
111pub unsafe fn PyDateTime_GET_MONTH(o: *mut PyObject) -> c_int {
112    let data = (*(o as *mut PyDateTime_Date)).data;
113    c_int::from(data[2])
114}
115
116#[inline]
117/// Retrieve the day component of a `PyDateTime_Date` or `PyDateTime_DateTime`.
118/// Returns a signed integer in the interval `[1, 31]`.
119pub unsafe fn PyDateTime_GET_DAY(o: *mut PyObject) -> c_int {
120    let data = (*(o as *mut PyDateTime_Date)).data;
121    c_int::from(data[3])
122}
123
124// Accessor macros for times
125macro_rules! _PyDateTime_GET_HOUR {
126    ($o: expr, $offset:expr) => {
127        c_int::from((*$o).data[$offset + 0])
128    };
129}
130
131macro_rules! _PyDateTime_GET_MINUTE {
132    ($o: expr, $offset:expr) => {
133        c_int::from((*$o).data[$offset + 1])
134    };
135}
136
137macro_rules! _PyDateTime_GET_SECOND {
138    ($o: expr, $offset:expr) => {
139        c_int::from((*$o).data[$offset + 2])
140    };
141}
142
143macro_rules! _PyDateTime_GET_MICROSECOND {
144    ($o: expr, $offset:expr) => {
145        (c_int::from((*$o).data[$offset + 3]) << 16)
146            | (c_int::from((*$o).data[$offset + 4]) << 8)
147            | (c_int::from((*$o).data[$offset + 5]))
148    };
149}
150
151macro_rules! _PyDateTime_GET_FOLD {
152    ($o: expr) => {
153        (*$o).fold
154    };
155}
156
157macro_rules! _PyDateTime_GET_TZINFO {
158    ($o: expr) => {
159        if (*$o).hastzinfo != 0 {
160            (*$o).tzinfo
161        } else {
162            $crate::Py_None()
163        }
164    };
165}
166
167// Accessor functions for DateTime
168#[inline]
169/// Retrieve the hour component of a `PyDateTime_DateTime`.
170/// Returns a signed integer in the interval `[0, 23]`
171pub unsafe fn PyDateTime_DATE_GET_HOUR(o: *mut PyObject) -> c_int {
172    _PyDateTime_GET_HOUR!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE)
173}
174
175#[inline]
176/// Retrieve the minute component of a `PyDateTime_DateTime`.
177/// Returns a signed integer in the interval `[0, 59]`
178pub unsafe fn PyDateTime_DATE_GET_MINUTE(o: *mut PyObject) -> c_int {
179    _PyDateTime_GET_MINUTE!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE)
180}
181
182#[inline]
183/// Retrieve the second component of a `PyDateTime_DateTime`.
184/// Returns a signed integer in the interval `[0, 59]`
185pub unsafe fn PyDateTime_DATE_GET_SECOND(o: *mut PyObject) -> c_int {
186    _PyDateTime_GET_SECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE)
187}
188
189#[inline]
190/// Retrieve the microsecond component of a `PyDateTime_DateTime`.
191/// Returns a signed integer in the interval `[0, 999999]`
192pub unsafe fn PyDateTime_DATE_GET_MICROSECOND(o: *mut PyObject) -> c_int {
193    _PyDateTime_GET_MICROSECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE)
194}
195
196#[inline]
197/// Retrieve the fold component of a `PyDateTime_DateTime`.
198/// Returns a signed integer in the interval `[0, 1]`
199pub unsafe fn PyDateTime_DATE_GET_FOLD(o: *mut PyObject) -> c_uchar {
200    _PyDateTime_GET_FOLD!(o as *mut PyDateTime_DateTime)
201}
202
203#[inline]
204/// Retrieve the tzinfo component of a `PyDateTime_DateTime`.
205/// Returns a pointer to a `PyObject` that should be either NULL or an instance
206/// of a `datetime.tzinfo` subclass.
207pub unsafe fn PyDateTime_DATE_GET_TZINFO(o: *mut PyObject) -> *mut PyObject {
208    _PyDateTime_GET_TZINFO!(o as *mut PyDateTime_DateTime)
209}
210
211// Accessor functions for Time
212#[inline]
213/// Retrieve the hour component of a `PyDateTime_Time`.
214/// Returns a signed integer in the interval `[0, 23]`
215pub unsafe fn PyDateTime_TIME_GET_HOUR(o: *mut PyObject) -> c_int {
216    _PyDateTime_GET_HOUR!((o as *mut PyDateTime_Time), 0)
217}
218
219#[inline]
220/// Retrieve the minute component of a `PyDateTime_Time`.
221/// Returns a signed integer in the interval `[0, 59]`
222pub unsafe fn PyDateTime_TIME_GET_MINUTE(o: *mut PyObject) -> c_int {
223    _PyDateTime_GET_MINUTE!((o as *mut PyDateTime_Time), 0)
224}
225
226#[inline]
227/// Retrieve the second component of a `PyDateTime_DateTime`.
228/// Returns a signed integer in the interval `[0, 59]`
229pub unsafe fn PyDateTime_TIME_GET_SECOND(o: *mut PyObject) -> c_int {
230    _PyDateTime_GET_SECOND!((o as *mut PyDateTime_Time), 0)
231}
232
233#[inline]
234/// Retrieve the microsecond component of a `PyDateTime_DateTime`.
235/// Returns a signed integer in the interval `[0, 999999]`
236pub unsafe fn PyDateTime_TIME_GET_MICROSECOND(o: *mut PyObject) -> c_int {
237    _PyDateTime_GET_MICROSECOND!((o as *mut PyDateTime_Time), 0)
238}
239
240#[inline]
241/// Retrieve the fold component of a `PyDateTime_Time`.
242/// Returns a signed integer in the interval `[0, 1]`
243pub unsafe fn PyDateTime_TIME_GET_FOLD(o: *mut PyObject) -> c_uchar {
244    _PyDateTime_GET_FOLD!(o as *mut PyDateTime_Time)
245}
246
247#[inline]
248/// Retrieve the tzinfo component of a `PyDateTime_Time`.
249/// Returns a pointer to a `PyObject` that should be either NULL or an instance
250/// of a `datetime.tzinfo` subclass.
251pub unsafe fn PyDateTime_TIME_GET_TZINFO(o: *mut PyObject) -> *mut PyObject {
252    _PyDateTime_GET_TZINFO!(o as *mut PyDateTime_Time)
253}
254
255// Accessor functions
256macro_rules! _access_field {
257    ($obj:expr, $type: ident, $field:ident) => {
258        (*($obj as *mut $type)).$field
259    };
260}
261
262// Accessor functions for PyDateTime_Delta
263macro_rules! _access_delta_field {
264    ($obj:expr, $field:ident) => {
265        _access_field!($obj, PyDateTime_Delta, $field)
266    };
267}
268
269#[inline]
270/// Retrieve the days component of a `PyDateTime_Delta`.
271///
272/// Returns a signed integer in the interval [-999999999, 999999999].
273///
274/// Note: This retrieves a component from the underlying structure, it is *not*
275/// a representation of the total duration of the structure.
276pub unsafe fn PyDateTime_DELTA_GET_DAYS(o: *mut PyObject) -> c_int {
277    _access_delta_field!(o, days)
278}
279
280#[inline]
281/// Retrieve the seconds component of a `PyDateTime_Delta`.
282///
283/// Returns a signed integer in the interval [0, 86399].
284///
285/// Note: This retrieves a component from the underlying structure, it is *not*
286/// a representation of the total duration of the structure.
287pub unsafe fn PyDateTime_DELTA_GET_SECONDS(o: *mut PyObject) -> c_int {
288    _access_delta_field!(o, seconds)
289}
290
291#[inline]
292/// Retrieve the seconds component of a `PyDateTime_Delta`.
293///
294/// Returns a signed integer in the interval [0, 999999].
295///
296/// Note: This retrieves a component from the underlying structure, it is *not*
297/// a representation of the total duration of the structure.
298pub unsafe fn PyDateTime_DELTA_GET_MICROSECONDS(o: *mut PyObject) -> c_int {
299    _access_delta_field!(o, microseconds)
300}
301
302#[repr(C)]
303#[derive(Debug, Copy, Clone)]
304pub struct PyDateTime_CAPI {
305    pub DateType: *mut PyTypeObject,
306    pub DateTimeType: *mut PyTypeObject,
307    pub TimeType: *mut PyTypeObject,
308    pub DeltaType: *mut PyTypeObject,
309    pub TZInfoType: *mut PyTypeObject,
310    pub TimeZone_UTC: *mut PyObject,
311    pub Date_FromDate: unsafe extern "C" fn(
312        year: c_int,
313        month: c_int,
314        day: c_int,
315        cls: *mut PyTypeObject,
316    ) -> *mut PyObject,
317    pub DateTime_FromDateAndTime: unsafe extern "C" fn(
318        year: c_int,
319        month: c_int,
320        day: c_int,
321        hour: c_int,
322        minute: c_int,
323        second: c_int,
324        microsecond: c_int,
325        tzinfo: *mut PyObject,
326        cls: *mut PyTypeObject,
327    ) -> *mut PyObject,
328    pub Time_FromTime: unsafe extern "C" fn(
329        hour: c_int,
330        minute: c_int,
331        second: c_int,
332        microsecond: c_int,
333        tzinfo: *mut PyObject,
334        cls: *mut PyTypeObject,
335    ) -> *mut PyObject,
336    pub Delta_FromDelta: unsafe extern "C" fn(
337        days: c_int,
338        seconds: c_int,
339        microseconds: c_int,
340        normalize: c_int,
341        cls: *mut PyTypeObject,
342    ) -> *mut PyObject,
343    pub TimeZone_FromTimeZone:
344        unsafe extern "C" fn(offset: *mut PyObject, name: *mut PyObject) -> *mut PyObject,
345
346    pub DateTime_FromTimestamp: unsafe extern "C" fn(
347        cls: *mut PyTypeObject,
348        args: *mut PyObject,
349        kwargs: *mut PyObject,
350    ) -> *mut PyObject,
351    pub Date_FromTimestamp:
352        unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject) -> *mut PyObject,
353    pub DateTime_FromDateAndTimeAndFold: unsafe extern "C" fn(
354        year: c_int,
355        month: c_int,
356        day: c_int,
357        hour: c_int,
358        minute: c_int,
359        second: c_int,
360        microsecond: c_int,
361        tzinfo: *mut PyObject,
362        fold: c_int,
363        cls: *mut PyTypeObject,
364    ) -> *mut PyObject,
365    pub Time_FromTimeAndFold: unsafe extern "C" fn(
366        hour: c_int,
367        minute: c_int,
368        second: c_int,
369        microsecond: c_int,
370        tzinfo: *mut PyObject,
371        fold: c_int,
372        cls: *mut PyTypeObject,
373    ) -> *mut PyObject,
374}
375
376// Python already shares this object between threads, so it's no more evil for us to do it too!
377unsafe impl Sync for PyDateTime_CAPI {}
378
379pub const PyDateTime_CAPSULE_NAME: &CStr = c"datetime.datetime_CAPI";
380
381/// Returns a pointer to a `PyDateTime_CAPI` instance
382///
383/// # Note
384/// This function will return a null pointer until
385/// `PyDateTime_IMPORT` is called
386#[inline]
387pub unsafe fn PyDateTimeAPI() -> *mut PyDateTime_CAPI {
388    *PyDateTimeAPI_impl.ptr.get()
389}
390
391/// Populates the `PyDateTimeAPI` object
392pub unsafe fn PyDateTime_IMPORT() {
393    if !PyDateTimeAPI_impl.once.is_completed() {
394        let py_datetime_c_api =
395            PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 1) as *mut PyDateTime_CAPI;
396
397        if py_datetime_c_api.is_null() {
398            return;
399        }
400
401        // Protect against race conditions when the datetime API is concurrently
402        // initialized in multiple threads. UnsafeCell.get() cannot panic so this
403        // won't panic either.
404        PyDateTimeAPI_impl.once.call_once(|| {
405            *PyDateTimeAPI_impl.ptr.get() = py_datetime_c_api;
406        });
407    }
408}
409
410#[inline]
411pub unsafe fn PyDateTime_TimeZone_UTC() -> *mut PyObject {
412    (*PyDateTimeAPI()).TimeZone_UTC
413}
414
415/// Type Check macros
416///
417/// These are bindings around the C API typecheck macros, all of them return
418/// `1` if True and `0` if False. In all type check macros, the argument (`op`)
419/// must not be `NULL`.
420#[inline]
421/// Check if `op` is a `PyDateTimeAPI.DateType` or subtype.
422pub unsafe fn PyDate_Check(op: *mut PyObject) -> c_int {
423    PyObject_TypeCheck(op, (*PyDateTimeAPI()).DateType) as c_int
424}
425
426#[inline]
427/// Check if `op`'s type is exactly `PyDateTimeAPI.DateType`.
428pub unsafe fn PyDate_CheckExact(op: *mut PyObject) -> c_int {
429    (Py_TYPE(op) == (*PyDateTimeAPI()).DateType) as c_int
430}
431
432#[inline]
433/// Check if `op` is a `PyDateTimeAPI.DateTimeType` or subtype.
434pub unsafe fn PyDateTime_Check(op: *mut PyObject) -> c_int {
435    PyObject_TypeCheck(op, (*PyDateTimeAPI()).DateTimeType) as c_int
436}
437
438#[inline]
439/// Check if `op`'s type is exactly `PyDateTimeAPI.DateTimeType`.
440pub unsafe fn PyDateTime_CheckExact(op: *mut PyObject) -> c_int {
441    (Py_TYPE(op) == (*PyDateTimeAPI()).DateTimeType) as c_int
442}
443
444#[inline]
445/// Check if `op` is a `PyDateTimeAPI.TimeType` or subtype.
446pub unsafe fn PyTime_Check(op: *mut PyObject) -> c_int {
447    PyObject_TypeCheck(op, (*PyDateTimeAPI()).TimeType) as c_int
448}
449
450#[inline]
451/// Check if `op`'s type is exactly `PyDateTimeAPI.TimeType`.
452pub unsafe fn PyTime_CheckExact(op: *mut PyObject) -> c_int {
453    (Py_TYPE(op) == (*PyDateTimeAPI()).TimeType) as c_int
454}
455
456#[inline]
457/// Check if `op` is a `PyDateTimeAPI.DetaType` or subtype.
458pub unsafe fn PyDelta_Check(op: *mut PyObject) -> c_int {
459    PyObject_TypeCheck(op, (*PyDateTimeAPI()).DeltaType) as c_int
460}
461
462#[inline]
463/// Check if `op`'s type is exactly `PyDateTimeAPI.DeltaType`.
464pub unsafe fn PyDelta_CheckExact(op: *mut PyObject) -> c_int {
465    (Py_TYPE(op) == (*PyDateTimeAPI()).DeltaType) as c_int
466}
467
468#[inline]
469/// Check if `op` is a `PyDateTimeAPI.TZInfoType` or subtype.
470pub unsafe fn PyTZInfo_Check(op: *mut PyObject) -> c_int {
471    PyObject_TypeCheck(op, (*PyDateTimeAPI()).TZInfoType) as c_int
472}
473
474#[inline]
475/// Check if `op`'s type is exactly `PyDateTimeAPI.TZInfoType`.
476pub unsafe fn PyTZInfo_CheckExact(op: *mut PyObject) -> c_int {
477    (Py_TYPE(op) == (*PyDateTimeAPI()).TZInfoType) as c_int
478}
479
480// skipped non-limited PyDate_FromDate
481// skipped non-limited PyDateTime_FromDateAndTime
482// skipped non-limited PyDateTime_FromDateAndTimeAndFold
483// skipped non-limited PyTime_FromTime
484// skipped non-limited PyTime_FromTimeAndFold
485// skipped non-limited PyDelta_FromDSU
486
487pub unsafe fn PyTimeZone_FromOffset(offset: *mut PyObject) -> *mut PyObject {
488    ((*PyDateTimeAPI()).TimeZone_FromTimeZone)(offset, std::ptr::null_mut())
489}
490
491pub unsafe fn PyTimeZone_FromOffsetAndName(
492    offset: *mut PyObject,
493    name: *mut PyObject,
494) -> *mut PyObject {
495    ((*PyDateTimeAPI()).TimeZone_FromTimeZone)(offset, name)
496}
497
498pub unsafe fn PyDateTime_FromTimestamp(args: *mut PyObject) -> *mut PyObject {
499    let f = (*PyDateTimeAPI()).DateTime_FromTimestamp;
500    f((*PyDateTimeAPI()).DateTimeType, args, std::ptr::null_mut())
501}
502
503pub unsafe fn PyDate_FromTimestamp(args: *mut PyObject) -> *mut PyObject {
504    let f = (*PyDateTimeAPI()).Date_FromTimestamp;
505    f((*PyDateTimeAPI()).DateType, args)
506}
507
508// Rust specific implementation details
509
510struct PyDateTimeAPISingleton {
511    once: Once,
512    ptr: UnsafeCell<*mut PyDateTime_CAPI>,
513}
514unsafe impl Sync for PyDateTimeAPISingleton {}
515
516static PyDateTimeAPI_impl: PyDateTimeAPISingleton = PyDateTimeAPISingleton {
517    once: Once::new(),
518    ptr: UnsafeCell::new(ptr::null_mut()),
519};