Skip to content

Commit

Permalink
ffi: support PyDateTime_TimeZone_UTC
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Apr 22, 2021
1 parent eed1b1a commit e95e73b
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Add FFI definition `Py_IS_TYPE`. [#1429](https://github.com/PyO3/pyo3/pull/1429)
- Add FFI definition `_Py_InitializeMain`. [#1473](https://github.com/PyO3/pyo3/pull/1473)
- Add tuple and unit struct support for `#[pyclass]` macro. [#1504](https://github.com/PyO3/pyo3/pull/1504)
- Add FFI definition `PyDateTime_TimeZone_UTC`. [#1572](https://github.com/PyO3/pyo3/pull/1572)

### Changed
- Change `PyTimeAcces::get_fold()` to return a `bool` instead of a `u8`. [#1397](https://github.com/PyO3/pyo3/pull/1397)
Expand Down
91 changes: 89 additions & 2 deletions src/ffi/datetime.rs
Expand Up @@ -399,13 +399,11 @@ pub struct PyDateTime_CAPI {
pub TimeZone_FromTimeZone:
unsafe extern "C" fn(offset: *mut PyObject, name: *mut PyObject) -> *mut PyObject,

// Defined for PyPy as `PyDateTime_FromTimestamp`
pub DateTime_FromTimestamp: unsafe extern "C" fn(
cls: *mut PyTypeObject,
args: *mut PyObject,
kwargs: *mut PyObject,
) -> *mut PyObject,
// Defined for PyPy as `PyDate_FromTimestamp`
pub Date_FromTimestamp:
unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject) -> *mut PyObject,
#[cfg(not(PyPy))]
Expand Down Expand Up @@ -447,6 +445,17 @@ pub static PyDateTimeAPI: _PyDateTimeAPI_impl = _PyDateTimeAPI_impl {
inner: GILOnceCell::new(),
};

/// Safe wrapper around the Python C-API global `PyDateTime_TimeZone_UTC`. This follows a similar
/// strategy as [`PyDateTimeAPI`]: the Python datetime C-API will automatically be imported if this
/// type is deferenced.
///
/// The type obtained by dereferencing this object is `&'static PyObject`. This may change in the
/// future to be a more specific type representing that this is a `datetime.timezone` object.
#[cfg(all(Py_3_7, not(PyPy)))]
pub static PyDateTime_TimeZone_UTC: _PyDateTime_TimeZone_UTC_impl = _PyDateTime_TimeZone_UTC_impl {
inner: &PyDateTimeAPI,
};

/// Populates the `PyDateTimeAPI` object
///
/// Unlike in C, this does *not* need to be actively invoked in Rust, which
Expand Down Expand Up @@ -559,13 +568,24 @@ pub unsafe fn PyTZInfo_CheckExact(op: *mut PyObject) -> c_int {
// skipped non-limited PyTimeZone_FromOffset
// skipped non-limited PyTimeZone_FromOffsetAndName

#[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"]
Expand All @@ -587,3 +607,70 @@ impl Deref for _PyDateTimeAPI_impl {
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);
dbg!(args.as_ref(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"
);
})
}
}

0 comments on commit e95e73b

Please sign in to comment.