Add PyDateTime component accessors

This commit is contained in:
Paul Ganssle 2018-08-09 10:50:46 -04:00
parent 0b39452276
commit ecf3aaafb2
No known key found for this signature in database
GPG key ID: CD54FCE3D964BEFB
5 changed files with 212 additions and 3 deletions

View file

@ -108,6 +108,7 @@ pub struct PyDateTime_CAPI {
// Type struct wrappers
const _PyDateTime_DATE_DATASIZE : usize = 4;
const _PyDateTime_DATETIME_DATASIZE: usize = 10;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
@ -118,6 +119,18 @@ pub struct PyDateTime_Date {
pub data: [c_uchar; _PyDateTime_DATE_DATASIZE],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct PyDateTime_DateTime {
pub ob_base: PyObject,
pub hashcode: Py_hash_t,
pub hastzinfo: c_char,
pub data: [c_uchar; _PyDateTime_DATETIME_DATASIZE],
#[cfg(Py_3_6)]
pub fold: c_uchar,
pub tzinfo: *mut PyObject
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct PyDateTime_Delta {
@ -200,6 +213,11 @@ pub unsafe fn PyDateTime_Date_GET_YEAR(o: *mut PyObject) -> c_int {
PyDateTime_GET_YEAR!(o as *mut PyDateTime_Date)
}
#[inline(always)]
pub unsafe fn PyDateTime_DateTime_GET_YEAR(o: *mut PyObject) -> c_int {
PyDateTime_GET_YEAR!(o as *mut PyDateTime_DateTime)
}
#[macro_export]
macro_rules! PyDateTime_GET_MONTH {
($o: expr) => {
@ -212,6 +230,11 @@ pub unsafe fn PyDateTime_Date_GET_MONTH(o: *mut PyObject) -> c_int {
PyDateTime_GET_MONTH!(o as *mut PyDateTime_Date)
}
#[inline(always)]
pub unsafe fn PyDateTime_DateTime_GET_MONTH(o: *mut PyObject) -> c_int {
PyDateTime_GET_MONTH!(o as *mut PyDateTime_DateTime)
}
#[macro_export]
macro_rules! PyDateTime_GET_DAY {
($o: expr) => {
@ -219,12 +242,88 @@ macro_rules! PyDateTime_GET_DAY {
}
}
#[inline(always)]
pub unsafe fn PyDateTime_Date_GET_DAY(o: *mut PyObject) -> c_int {
PyDateTime_GET_DAY!(o as *mut PyDateTime_Date)
}
#[inline(always)]
pub unsafe fn PyDateTime_DateTime_GET_DAY(o: *mut PyObject) -> c_int {
PyDateTime_GET_DAY!(o as *mut PyDateTime_DateTime)
}
// Accessor macros for times
macro_rules! _PyDateTime_GET_HOUR {
($o: expr, $offset:expr) => {
(*$o).data[$offset + 0] as c_int
}
}
macro_rules! _PyDateTime_GET_MINUTE {
($o: expr, $offset:expr) => {
(*$o).data[$offset + 1] as c_int
}
}
macro_rules! _PyDateTime_GET_SECOND {
($o: expr, $offset:expr) => {
(*$o).data[$offset + 2] as c_int
}
}
macro_rules! _PyDateTime_GET_MICROSECOND {
($o: expr, $offset:expr) => {
(((*$o).data[$offset + 3] as c_int) << 16) |
(((*$o).data[$offset + 4] as c_int) << 8) |
((*$o).data[$offset + 5] as c_int)
}
}
#[cfg(Py_3_6)]
macro_rules! _PyDateTime_GET_FOLD{
($o: expr) => {
(*$o).fold
}
}
macro_rules! _PyDateTime_GET_TZINFO{
($o: expr) => {
(*$o).tzinfo
}
}
// Accessor functions for DateTime
#[inline(always)]
pub unsafe fn PyDateTime_DATE_GET_HOUR(o: *mut PyObject) -> c_int {
_PyDateTime_GET_HOUR!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE)
}
#[inline(always)]
pub unsafe fn PyDateTime_DATE_GET_MINUTE(o: *mut PyObject) -> c_int {
_PyDateTime_GET_MINUTE!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE)
}
#[inline(always)]
pub unsafe fn PyDateTime_DATE_GET_SECOND(o: *mut PyObject) -> c_int {
_PyDateTime_GET_SECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE)
}
#[inline(always)]
pub unsafe fn PyDateTime_DATE_GET_MICROSECOND(o: *mut PyObject) -> c_int {
_PyDateTime_GET_MICROSECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE)
}
#[cfg(Py_3_6)]
#[inline(always)]
pub unsafe fn PyDateTime_DATE_GET_FOLD(o: *mut PyObject) -> c_uchar {
_PyDateTime_GET_FOLD!(o as *mut PyDateTime_DateTime)
}
#[inline(always)]
pub unsafe fn PyDateTime_DATE_GET_TZINFO(o: *mut PyObject) -> *mut PyObject {
_PyDateTime_GET_TZINFO!(o as *mut PyDateTime_DateTime)
}
// Accessor functions for PyDateTime_Delta
macro_rules! _access_delta_field {

View file

@ -5,10 +5,17 @@ use ffi::{PyDateTimeAPI};
use ffi::{PyDateTime_DateType, PyDate_Check};
use ffi::{PyDateTime_Date_GET_YEAR, PyDateTime_Date_GET_MONTH, PyDateTime_Date_GET_DAY};
use ffi::{PyDateTime_DateTimeType, PyDateTime_Check};
use ffi::{PyDateTime_DateTime_GET_YEAR, PyDateTime_DateTime_GET_MONTH, PyDateTime_DateTime_GET_DAY};
use ffi::{PyDateTime_DATE_GET_HOUR, PyDateTime_DATE_GET_MINUTE,
PyDateTime_DATE_GET_SECOND, PyDateTime_DATE_GET_MICROSECOND};
use ffi::{PyDateTime_DeltaType, PyDelta_Check};
use ffi::{PyDateTime_DELTA_GET_DAYS, PyDateTime_DELTA_GET_SECONDS, PyDateTime_DELTA_GET_MICROSECONDS};
use ffi::{PyDateTime_TimeType, PyTime_Check};
use ffi::{PyDateTime_TZInfoType, PyTZInfo_Check};
#[cfg(Py_3_6)]
use ffi::{PyDateTime_DATE_GET_FOLD};
use python::{Python, ToPyPointer};
use instance::Py;
@ -26,6 +33,16 @@ pub trait PyDeltaComponentAccess {
}
pub trait PyTimeComponentAccess {
fn get_hour(&self) -> u32;
fn get_minute(&self) -> u32;
fn get_second(&self) -> u32;
fn get_microsecond(&self) -> u32;
#[cfg(Py_3_6)]
fn get_fold(&self) -> u8;
}
// datetime.date bindings
pub struct PyDate(PyObject);
pyobject_native_type!(PyDate, PyDateTime_DateType, PyDate_Check);
@ -73,6 +90,7 @@ impl PyDateComponentAccess for PyDate {
}
}
// datetime.datetime bindings
pub struct PyDateTime(PyObject);
pyobject_native_type!(PyDateTime, PyDateTime_DateTimeType, PyDateTime_Check);
@ -108,8 +126,63 @@ impl PyDateTime {
Py::from_owned_ptr_or_err(py, ptr)
}
}
}
impl PyDateComponentAccess for PyDateTime {
fn get_year(&self) -> u32 {
unsafe {
PyDateTime_DateTime_GET_YEAR(self.as_ptr()) as u32
}
}
fn get_month(&self) -> u32 {
unsafe {
PyDateTime_DateTime_GET_MONTH(self.as_ptr()) as u32
}
}
fn get_day(&self) -> u32 {
unsafe {
PyDateTime_DateTime_GET_DAY(self.as_ptr()) as u32
}
}
}
impl PyTimeComponentAccess for PyDateTime {
fn get_hour(&self) -> u32 {
unsafe {
PyDateTime_DATE_GET_HOUR(self.as_ptr()) as u32
}
}
fn get_minute(&self) -> u32 {
unsafe {
PyDateTime_DATE_GET_MINUTE(self.as_ptr()) as u32
}
}
fn get_second(&self) -> u32 {
unsafe {
PyDateTime_DATE_GET_SECOND(self.as_ptr()) as u32
}
}
fn get_microsecond(&self) -> u32 {
unsafe {
PyDateTime_DATE_GET_MICROSECOND(self.as_ptr()) as u32
}
}
#[cfg(Py_3_6)]
fn get_fold(&self) -> u8 {
unsafe {
PyDateTime_DATE_GET_FOLD(self.as_ptr()) as u8
}
}
}
// datetime.time
pub struct PyTime(PyObject);
pyobject_native_type!(PyTime, PyDateTime_TimeType, PyTime_Check);

View file

@ -6,7 +6,7 @@ mod exc_impl;
pub use self::boolobject::PyBool;
pub use self::bytearray::PyByteArray;
pub use self::datetime::{PyDate, PyTime, PyDateTime, PyDelta, PyTzInfo};
pub use self::datetime::{PyDateComponentAccess};
pub use self::datetime::{PyDateComponentAccess, PyTimeComponentAccess};
pub use self::datetime::{PyDeltaComponentAccess};
pub use self::dict::PyDict;
pub use self::floatob::PyFloat;

View file

@ -9,7 +9,7 @@ use pyo3::prelude::{pyfunction, pymodinit};
use pyo3::prelude::{PyObject};
use pyo3::prelude::{PyModule};
use pyo3::prelude::{PyDate, PyTime, PyDateTime, PyDelta, PyTzInfo};
use pyo3::prelude::{PyDateComponentAccess};
use pyo3::prelude::{PyDateComponentAccess, PyTimeComponentAccess};
use pyo3::prelude::{PyDeltaComponentAccess};
use pyo3::prelude::{PyTuple, PyDict};
@ -67,6 +67,21 @@ fn make_datetime(py: Python, year: u32, month: u32, day: u32,
PyDateTime::new(py, year, month, day, hour, minute, second, microsecond, &tzi)
}
#[pyfunction]
fn get_datetime_tuple(py: Python, dt: &PyDateTime) -> Py<PyTuple> {
PyTuple::new(py, &[dt.get_year(), dt.get_month(), dt.get_day(),
dt.get_hour(), dt.get_minute(), dt.get_second(),
dt.get_microsecond()])
}
#[cfg(Py_3_6)]
#[pyfunction]
fn get_datetime_tuple_fold(py: Python, dt: &PyDateTime) -> Py<PyTuple> {
PyTuple::new(py, &[dt.get_year(), dt.get_month(), dt.get_day(),
dt.get_hour(), dt.get_minute(), dt.get_second(),
dt.get_microsecond(), dt.get_fold() as u32])
}
#[pyfunction]
fn datetime_from_timestamp(py: Python, ts: f64, tz: Option<&PyTzInfo>) -> PyResult<Py<PyDateTime>> {
let timestamp : PyObject = ts.to_object(py);
@ -102,12 +117,14 @@ fn datetime(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_function!(make_delta))?;
m.add_function(wrap_function!(get_delta_tuple))?;
m.add_function(wrap_function!(make_datetime))?;
m.add_function(wrap_function!(get_datetime_tuple))?;
m.add_function(wrap_function!(datetime_from_timestamp))?;
// Python 3.6+ functions
#[cfg(Py_3_6)]
{
m.add_function(wrap_function!(time_with_fold));
m.add_function(wrap_function!(get_datetime_tuple_fold));
}
Ok(())

View file

@ -119,6 +119,26 @@ def test_datetime(args, kwargs):
assert act.tzinfo is exp.tzinfo
@given(dt=st.datetimes())
def test_datetime_tuple(dt):
act = rdt.get_datetime_tuple(dt)
exp = dt.timetuple()[0:6] + (dt.microsecond, )
assert act == exp
@pytest.mark.skipif(not HAS_FOLD, reason="Feature not available before 3.6")
@given(dt=st.datetimes())
def test_datetime_tuple_fold(dt):
dt_fold = dt.replace(fold=0)
dt_nofold = dt.replace(fold=1)
for dt in (dt_fold, dt_nofold):
act = rdt.get_datetime_tuple_fold(dt)
exp = dt.timetuple()[0:6] + (dt.microsecond, dt.fold)
assert act == exp
def test_invalid_datetime_fails():
with pytest.raises(ValueError):
rdt.make_datetime(2011, 1, 42, 0, 0, 0, 0)