use crate::ffi::{PyObject, PyTypeObject};
use crate::ffi::{PyObject_TypeCheck, Py_TYPE};
use crate::once_cell::GILOnceCell;
use crate::Python;
use std::ops::Deref;
use std::os::raw::{c_char, c_int, c_uchar};
#[cfg(not(PyPy))]
use {
crate::ffi::{PyCapsule_Import, Py_hash_t},
std::ffi::CString,
};
const _PyDateTime_DATE_DATASIZE: usize = 4;
const _PyDateTime_TIME_DATASIZE: usize = 6;
const _PyDateTime_DATETIME_DATASIZE: usize = 10;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct PyDateTime_Delta {
pub ob_base: PyObject,
#[cfg(not(PyPy))]
pub hashcode: Py_hash_t,
pub days: c_int,
pub seconds: c_int,
pub microseconds: c_int,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct PyDateTime_Time {
pub ob_base: PyObject,
#[cfg(not(PyPy))]
pub hashcode: Py_hash_t,
pub hastzinfo: c_char,
#[cfg(not(PyPy))]
pub data: [c_uchar; _PyDateTime_TIME_DATASIZE],
#[cfg(not(PyPy))]
pub fold: c_uchar,
pub tzinfo: *mut PyObject,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct PyDateTime_Date {
pub ob_base: PyObject,
#[cfg(not(PyPy))]
pub hashcode: Py_hash_t,
pub hastzinfo: c_char,
pub data: [c_uchar; _PyDateTime_DATE_DATASIZE],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct PyDateTime_DateTime {
pub ob_base: PyObject,
#[cfg(not(PyPy))]
pub hashcode: Py_hash_t,
pub hastzinfo: c_char,
#[cfg(not(PyPy))]
pub data: [c_uchar; _PyDateTime_DATETIME_DATASIZE],
#[cfg(not(PyPy))]
pub fold: c_uchar,
pub tzinfo: *mut PyObject,
}
#[inline]
#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_GET_YEAR(o: *mut PyObject) -> c_int {
let d = *(o as *mut PyDateTime_Date);
c_int::from(d.data[0]) << 8 | c_int::from(d.data[1])
}
#[inline]
#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_GET_MONTH(o: *mut PyObject) -> c_int {
let d = *(o as *mut PyDateTime_Date);
c_int::from(d.data[2])
}
#[inline]
#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_GET_DAY(o: *mut PyObject) -> c_int {
let d = *(o as *mut PyDateTime_Date);
c_int::from(d.data[3])
}
#[cfg(not(PyPy))]
macro_rules! _PyDateTime_GET_HOUR {
($o: expr, $offset:expr) => {
c_int::from((*$o).data[$offset + 0])
};
}
#[cfg(not(PyPy))]
macro_rules! _PyDateTime_GET_MINUTE {
($o: expr, $offset:expr) => {
c_int::from((*$o).data[$offset + 1])
};
}
#[cfg(not(PyPy))]
macro_rules! _PyDateTime_GET_SECOND {
($o: expr, $offset:expr) => {
c_int::from((*$o).data[$offset + 2])
};
}
#[cfg(not(PyPy))]
macro_rules! _PyDateTime_GET_MICROSECOND {
($o: expr, $offset:expr) => {
(c_int::from((*$o).data[$offset + 3]) << 16)
| (c_int::from((*$o).data[$offset + 4]) << 8)
| (c_int::from((*$o).data[$offset + 5]))
};
}
#[cfg(not(PyPy))]
macro_rules! _PyDateTime_GET_FOLD {
($o: expr) => {
(*$o).fold
};
}
#[cfg(not(PyPy))]
macro_rules! _PyDateTime_GET_TZINFO {
($o: expr) => {
(*$o).tzinfo
};
}
#[inline]
#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_DATE_GET_HOUR(o: *mut PyObject) -> c_int {
_PyDateTime_GET_HOUR!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE)
}
#[inline]
#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_DATE_GET_MINUTE(o: *mut PyObject) -> c_int {
_PyDateTime_GET_MINUTE!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE)
}
#[inline]
#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_DATE_GET_SECOND(o: *mut PyObject) -> c_int {
_PyDateTime_GET_SECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE)
}
#[inline]
#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_DATE_GET_MICROSECOND(o: *mut PyObject) -> c_int {
_PyDateTime_GET_MICROSECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE)
}
#[inline]
#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_DATE_GET_FOLD(o: *mut PyObject) -> c_uchar {
_PyDateTime_GET_FOLD!(o as *mut PyDateTime_DateTime)
}
#[inline]
#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_DATE_GET_TZINFO(o: *mut PyObject) -> *mut PyObject {
_PyDateTime_GET_TZINFO!(o as *mut PyDateTime_DateTime)
}
#[inline]
#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_TIME_GET_HOUR(o: *mut PyObject) -> c_int {
_PyDateTime_GET_HOUR!((o as *mut PyDateTime_Time), 0)
}
#[inline]
#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_TIME_GET_MINUTE(o: *mut PyObject) -> c_int {
_PyDateTime_GET_MINUTE!((o as *mut PyDateTime_Time), 0)
}
#[inline]
#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_TIME_GET_SECOND(o: *mut PyObject) -> c_int {
_PyDateTime_GET_SECOND!((o as *mut PyDateTime_Time), 0)
}
#[inline]
#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_TIME_GET_MICROSECOND(o: *mut PyObject) -> c_int {
_PyDateTime_GET_MICROSECOND!((o as *mut PyDateTime_Time), 0)
}
#[cfg(not(PyPy))]
#[inline]
pub unsafe fn PyDateTime_TIME_GET_FOLD(o: *mut PyObject) -> c_uchar {
_PyDateTime_GET_FOLD!(o as *mut PyDateTime_Time)
}
#[inline]
#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_TIME_GET_TZINFO(o: *mut PyObject) -> *mut PyObject {
_PyDateTime_GET_TZINFO!(o as *mut PyDateTime_Time)
}
#[cfg(not(PyPy))]
macro_rules! _access_field {
($obj:expr, $type: ident, $field:ident) => {
(*($obj as *mut $type)).$field
};
}
#[cfg(not(PyPy))]
macro_rules! _access_delta_field {
($obj:expr, $field:ident) => {
_access_field!($obj, PyDateTime_Delta, $field)
};
}
#[inline]
#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_DELTA_GET_DAYS(o: *mut PyObject) -> c_int {
_access_delta_field!(o, days)
}
#[inline]
#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_DELTA_GET_SECONDS(o: *mut PyObject) -> c_int {
_access_delta_field!(o, seconds)
}
#[inline]
#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_DELTA_GET_MICROSECONDS(o: *mut PyObject) -> c_int {
_access_delta_field!(o, microseconds)
}
#[cfg(PyPy)]
extern "C" {
#[link_name = "PyPyDateTime_GET_YEAR"]
pub fn PyDateTime_GET_YEAR(o: *mut PyObject) -> c_int;
#[link_name = "PyPyDateTime_GET_MONTH"]
pub fn PyDateTime_GET_MONTH(o: *mut PyObject) -> c_int;
#[link_name = "PyPyDateTime_GET_DAY"]
pub fn PyDateTime_GET_DAY(o: *mut PyObject) -> c_int;
#[link_name = "PyPyDateTime_DATE_GET_HOUR"]
pub fn PyDateTime_DATE_GET_HOUR(o: *mut PyObject) -> c_int;
#[link_name = "PyPyDateTime_DATE_GET_MINUTE"]
pub fn PyDateTime_DATE_GET_MINUTE(o: *mut PyObject) -> c_int;
#[link_name = "PyPyDateTime_DATE_GET_SECOND"]
pub fn PyDateTime_DATE_GET_SECOND(o: *mut PyObject) -> c_int;
#[link_name = "PyPyDateTime_DATE_GET_MICROSECOND"]
pub fn PyDateTime_DATE_GET_MICROSECOND(o: *mut PyObject) -> c_int;
#[link_name = "PyPyDateTime_TIME_GET_HOUR"]
pub fn PyDateTime_TIME_GET_HOUR(o: *mut PyObject) -> c_int;
#[link_name = "PyPyDateTime_TIME_GET_MINUTE"]
pub fn PyDateTime_TIME_GET_MINUTE(o: *mut PyObject) -> c_int;
#[link_name = "PyPyDateTime_TIME_GET_SECOND"]
pub fn PyDateTime_TIME_GET_SECOND(o: *mut PyObject) -> c_int;
#[link_name = "PyPyDateTime_TIME_GET_MICROSECOND"]
pub fn PyDateTime_TIME_GET_MICROSECOND(o: *mut PyObject) -> c_int;
#[link_name = "PyPyDateTime_DELTA_GET_DAYS"]
pub fn PyDateTime_DELTA_GET_DAYS(o: *mut PyObject) -> c_int;
#[link_name = "PyPyDateTime_DELTA_GET_SECONDS"]
pub fn PyDateTime_DELTA_GET_SECONDS(o: *mut PyObject) -> c_int;
#[link_name = "PyPyDateTime_DELTA_GET_MICROSECONDS"]
pub fn PyDateTime_DELTA_GET_MICROSECONDS(o: *mut PyObject) -> c_int;
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct PyDateTime_CAPI {
pub DateType: *mut PyTypeObject,
pub DateTimeType: *mut PyTypeObject,
pub TimeType: *mut PyTypeObject,
pub DeltaType: *mut PyTypeObject,
pub TZInfoType: *mut PyTypeObject,
#[cfg(all(Py_3_7, not(PyPy)))]
pub TimeZone_UTC: *mut PyObject,
pub Date_FromDate: unsafe extern "C" fn(
year: c_int,
month: c_int,
day: c_int,
cls: *mut PyTypeObject,
) -> *mut PyObject,
pub DateTime_FromDateAndTime: unsafe extern "C" fn(
year: c_int,
month: c_int,
day: c_int,
hour: c_int,
minute: c_int,
second: c_int,
microsecond: c_int,
tzinfo: *mut PyObject,
cls: *mut PyTypeObject,
) -> *mut PyObject,
pub Time_FromTime: unsafe extern "C" fn(
hour: c_int,
minute: c_int,
second: c_int,
microsecond: c_int,
tzinfo: *mut PyObject,
cls: *mut PyTypeObject,
) -> *mut PyObject,
pub Delta_FromDelta: unsafe extern "C" fn(
days: c_int,
seconds: c_int,
microseconds: c_int,
normalize: c_int,
cls: *mut PyTypeObject,
) -> *mut PyObject,
#[cfg(all(Py_3_7, not(PyPy)))]
pub TimeZone_FromTimeZone:
unsafe extern "C" fn(offset: *mut PyObject, name: *mut PyObject) -> *mut PyObject,
pub DateTime_FromTimestamp: unsafe extern "C" fn(
cls: *mut PyTypeObject,
args: *mut PyObject,
kwargs: *mut PyObject,
) -> *mut PyObject,
pub Date_FromTimestamp:
unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject) -> *mut PyObject,
#[cfg(not(PyPy))]
pub DateTime_FromDateAndTimeAndFold: unsafe extern "C" fn(
year: c_int,
month: c_int,
day: c_int,
hour: c_int,
minute: c_int,
second: c_int,
microsecond: c_int,
tzinfo: *mut PyObject,
fold: c_int,
cls: *mut PyTypeObject,
) -> *mut PyObject,
#[cfg(not(PyPy))]
pub Time_FromTimeAndFold: unsafe extern "C" fn(
hour: c_int,
minute: c_int,
second: c_int,
microsecond: c_int,
tzinfo: *mut PyObject,
fold: c_int,
cls: *mut PyTypeObject,
) -> *mut PyObject,
}
unsafe impl Sync for PyDateTime_CAPI {}
pub static PyDateTimeAPI: _PyDateTimeAPI_impl = _PyDateTimeAPI_impl {
inner: GILOnceCell::new(),
};
#[cfg(all(Py_3_7, not(PyPy)))]
pub static PyDateTime_TimeZone_UTC: _PyDateTime_TimeZone_UTC_impl = _PyDateTime_TimeZone_UTC_impl {
inner: &PyDateTimeAPI,
};
pub unsafe fn PyDateTime_IMPORT() -> &'static PyDateTime_CAPI {
PyDateTimeAPI
.inner
.get_or_init(Python::assume_gil_acquired(), || {
Python::with_gil(|_py| {
#[cfg(PyPy)]
let py_datetime_c_api = PyDateTime_Import();
#[cfg(not(PyPy))]
let py_datetime_c_api = {
let PyDateTime_CAPSULE_NAME = CString::new("datetime.datetime_CAPI").unwrap();
&*(PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 1)
as *const PyDateTime_CAPI)
};
py_datetime_c_api
})
})
}
#[inline]
pub unsafe fn PyDate_Check(op: *mut PyObject) -> c_int {
PyObject_TypeCheck(op, PyDateTimeAPI.DateType) as c_int
}
#[inline]
pub unsafe fn PyDate_CheckExact(op: *mut PyObject) -> c_int {
(Py_TYPE(op) == PyDateTimeAPI.DateType) as c_int
}
#[inline]
pub unsafe fn PyDateTime_Check(op: *mut PyObject) -> c_int {
PyObject_TypeCheck(op, PyDateTimeAPI.DateTimeType) as c_int
}
#[inline]
pub unsafe fn PyDateTime_CheckExact(op: *mut PyObject) -> c_int {
(Py_TYPE(op) == PyDateTimeAPI.DateTimeType) as c_int
}
#[inline]
pub unsafe fn PyTime_Check(op: *mut PyObject) -> c_int {
PyObject_TypeCheck(op, PyDateTimeAPI.TimeType) as c_int
}
#[inline]
pub unsafe fn PyTime_CheckExact(op: *mut PyObject) -> c_int {
(Py_TYPE(op) == PyDateTimeAPI.TimeType) as c_int
}
#[inline]
pub unsafe fn PyDelta_Check(op: *mut PyObject) -> c_int {
PyObject_TypeCheck(op, PyDateTimeAPI.DeltaType) as c_int
}
#[inline]
pub unsafe fn PyDelta_CheckExact(op: *mut PyObject) -> c_int {
(Py_TYPE(op) == PyDateTimeAPI.DeltaType) as c_int
}
#[inline]
pub unsafe fn PyTZInfo_Check(op: *mut PyObject) -> c_int {
PyObject_TypeCheck(op, PyDateTimeAPI.TZInfoType) as c_int
}
#[inline]
pub unsafe fn PyTZInfo_CheckExact(op: *mut PyObject) -> c_int {
(Py_TYPE(op) == PyDateTimeAPI.TZInfoType) as c_int
}
#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_FromTimestamp(args: *mut PyObject) -> *mut PyObject {
(PyDateTimeAPI.DateTime_FromTimestamp)(PyDateTimeAPI.DateTimeType, args, std::ptr::null_mut())
}
#[cfg(not(PyPy))]
pub unsafe fn PyDate_FromTimestamp(args: *mut PyObject) -> *mut PyObject {
(PyDateTimeAPI.Date_FromTimestamp)(PyDateTimeAPI.DateType, args)
}
#[cfg(PyPy)]
extern "C" {
#[link_name = "PyPyDate_FromTimestamp"]
pub fn PyDate_FromTimestamp(args: *mut PyObject) -> *mut PyObject;
#[link_name = "PyPyDateTime_FromTimestamp"]
pub fn PyDateTime_FromTimestamp(args: *mut PyObject) -> *mut PyObject;
}
#[cfg(PyPy)]
extern "C" {
#[link_name = "_PyPyDateTime_Import"]
pub fn PyDateTime_Import() -> &'static PyDateTime_CAPI;
}
#[doc(hidden)]
pub struct _PyDateTimeAPI_impl {
inner: GILOnceCell<&'static PyDateTime_CAPI>,
}
impl Deref for _PyDateTimeAPI_impl {
type Target = PyDateTime_CAPI;
#[inline]
fn deref(&self) -> &'static PyDateTime_CAPI {
unsafe { PyDateTime_IMPORT() }
}
}
#[doc(hidden)]
#[cfg(all(Py_3_7, not(PyPy)))]
pub struct _PyDateTime_TimeZone_UTC_impl {
inner: &'static _PyDateTimeAPI_impl,
}
#[cfg(all(Py_3_7, not(PyPy)))]
impl Deref for _PyDateTime_TimeZone_UTC_impl {
type Target = crate::PyObject;
#[inline]
fn deref(&self) -> &crate::PyObject {
unsafe {
&*((&self.inner.TimeZone_UTC) as *const *mut crate::ffi::PyObject
as *const crate::PyObject)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{py_run, AsPyPointer, IntoPy, Py, PyAny, Python};
#[test]
fn test_datetime_fromtimestamp() {
Python::with_gil(|py| {
let args: Py<PyAny> = (100,).into_py(py);
unsafe { PyDateTime_IMPORT() };
let dt: &PyAny = unsafe { py.from_owned_ptr(PyDateTime_FromTimestamp(args.as_ptr())) };
py_run!(
py,
dt,
"import datetime; assert dt == datetime.datetime.fromtimestamp(100)"
);
})
}
#[test]
fn test_date_fromtimestamp() {
Python::with_gil(|py| {
let args: Py<PyAny> = (100,).into_py(py);
unsafe { PyDateTime_IMPORT() };
let dt: &PyAny = unsafe { py.from_owned_ptr(PyDate_FromTimestamp(args.as_ptr())) };
py_run!(
py,
dt,
"import datetime; assert dt == datetime.date.fromtimestamp(100)"
);
})
}
#[test]
#[cfg(all(Py_3_7, not(PyPy)))]
fn test_utc_timezone() {
Python::with_gil(|py| {
let utc_timezone = PyDateTime_TimeZone_UTC.as_ref(py);
py_run!(
py,
utc_timezone,
"import datetime; assert utc_timezone is datetime.timezone.utc"
);
})
}
}