use crate::err::PyResult;
use crate::ffi::{
self, PyDateTime_CAPI, PyDateTime_FromTimestamp, PyDateTime_IMPORT, PyDate_FromTimestamp,
};
use crate::ffi::{
PyDateTime_DATE_GET_FOLD, PyDateTime_DATE_GET_HOUR, PyDateTime_DATE_GET_MICROSECOND,
PyDateTime_DATE_GET_MINUTE, PyDateTime_DATE_GET_SECOND,
};
#[cfg(GraalPy)]
use crate::ffi::{PyDateTime_DATE_GET_TZINFO, PyDateTime_TIME_GET_TZINFO, Py_IsNone};
use crate::ffi::{
PyDateTime_DELTA_GET_DAYS, PyDateTime_DELTA_GET_MICROSECONDS, PyDateTime_DELTA_GET_SECONDS,
};
use crate::ffi::{PyDateTime_GET_DAY, PyDateTime_GET_MONTH, PyDateTime_GET_YEAR};
use crate::ffi::{
PyDateTime_TIME_GET_FOLD, PyDateTime_TIME_GET_HOUR, PyDateTime_TIME_GET_MICROSECOND,
PyDateTime_TIME_GET_MINUTE, PyDateTime_TIME_GET_SECOND,
};
use crate::ffi_ptr_ext::FfiPtrExt;
#[cfg(feature = "gil-refs")]
use crate::instance::PyNativeType;
use crate::py_result_ext::PyResultExt;
use crate::types::any::PyAnyMethods;
use crate::types::PyTuple;
use crate::{Bound, IntoPy, Py, PyAny, PyErr, Python};
use std::os::raw::c_int;
#[cfg(feature = "chrono")]
use std::ptr;
fn ensure_datetime_api(py: Python<'_>) -> PyResult<&'static PyDateTime_CAPI> {
if let Some(api) = unsafe { pyo3_ffi::PyDateTimeAPI().as_ref() } {
Ok(api)
} else {
unsafe {
PyDateTime_IMPORT();
pyo3_ffi::PyDateTimeAPI().as_ref()
}
.ok_or_else(|| PyErr::fetch(py))
}
}
fn expect_datetime_api(py: Python<'_>) -> &'static PyDateTime_CAPI {
ensure_datetime_api(py).expect("failed to import `datetime` C API")
}
macro_rules! ffi_fun_with_autoinit {
($(#[$outer:meta] unsafe fn $name: ident($arg: ident: *mut PyObject) -> $ret: ty;)*) => {
$(
#[$outer]
#[allow(non_snake_case)]
unsafe fn $name($arg: *mut crate::ffi::PyObject) -> $ret {
let _ = ensure_datetime_api(Python::assume_gil_acquired());
crate::ffi::$name($arg)
}
)*
};
}
ffi_fun_with_autoinit! {
unsafe fn PyDate_Check(op: *mut PyObject) -> c_int;
unsafe fn PyDateTime_Check(op: *mut PyObject) -> c_int;
unsafe fn PyTime_Check(op: *mut PyObject) -> c_int;
unsafe fn PyDelta_Check(op: *mut PyObject) -> c_int;
unsafe fn PyTZInfo_Check(op: *mut PyObject) -> c_int;
}
pub trait PyDateAccess {
fn get_year(&self) -> i32;
fn get_month(&self) -> u8;
fn get_day(&self) -> u8;
}
pub trait PyDeltaAccess {
fn get_days(&self) -> i32;
fn get_seconds(&self) -> i32;
fn get_microseconds(&self) -> i32;
}
pub trait PyTimeAccess {
fn get_hour(&self) -> u8;
fn get_minute(&self) -> u8;
fn get_second(&self) -> u8;
fn get_microsecond(&self) -> u32;
fn get_fold(&self) -> bool;
}
pub trait PyTzInfoAccess<'py> {
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`get_tzinfo` will be replaced by `get_tzinfo_bound` in a future PyO3 version"
)]
fn get_tzinfo(&self) -> Option<&'py PyTzInfo> {
self.get_tzinfo_bound().map(Bound::into_gil_ref)
}
fn get_tzinfo_bound(&self) -> Option<Bound<'py, PyTzInfo>>;
}
#[repr(transparent)]
pub struct PyDate(PyAny);
pyobject_native_type!(
PyDate,
crate::ffi::PyDateTime_Date,
|py| expect_datetime_api(py).DateType,
#module=Some("datetime"),
#checkfunction=PyDate_Check
);
impl PyDate {
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyDate::new` will be replaced by `PyDate::new_bound` in a future PyO3 version"
)]
pub fn new(py: Python<'_>, year: i32, month: u8, day: u8) -> PyResult<&PyDate> {
Self::new_bound(py, year, month, day).map(Bound::into_gil_ref)
}
pub fn new_bound(py: Python<'_>, year: i32, month: u8, day: u8) -> PyResult<Bound<'_, PyDate>> {
let api = ensure_datetime_api(py)?;
unsafe {
(api.Date_FromDate)(year, c_int::from(month), c_int::from(day), api.DateType)
.assume_owned_or_err(py)
.downcast_into_unchecked()
}
}
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyDate::from_timestamp` will be replaced by `PyDate::from_timestamp_bound` in a future PyO3 version"
)]
pub fn from_timestamp(py: Python<'_>, timestamp: i64) -> PyResult<&PyDate> {
Self::from_timestamp_bound(py, timestamp).map(Bound::into_gil_ref)
}
pub fn from_timestamp_bound(py: Python<'_>, timestamp: i64) -> PyResult<Bound<'_, PyDate>> {
let time_tuple = PyTuple::new_bound(py, [timestamp]);
let _api = ensure_datetime_api(py)?;
unsafe {
PyDate_FromTimestamp(time_tuple.as_ptr())
.assume_owned_or_err(py)
.downcast_into_unchecked()
}
}
}
#[cfg(feature = "gil-refs")]
impl PyDateAccess for PyDate {
fn get_year(&self) -> i32 {
self.as_borrowed().get_year()
}
fn get_month(&self) -> u8 {
self.as_borrowed().get_month()
}
fn get_day(&self) -> u8 {
self.as_borrowed().get_day()
}
}
impl PyDateAccess for Bound<'_, PyDate> {
fn get_year(&self) -> i32 {
unsafe { PyDateTime_GET_YEAR(self.as_ptr()) }
}
fn get_month(&self) -> u8 {
unsafe { PyDateTime_GET_MONTH(self.as_ptr()) as u8 }
}
fn get_day(&self) -> u8 {
unsafe { PyDateTime_GET_DAY(self.as_ptr()) as u8 }
}
}
#[repr(transparent)]
pub struct PyDateTime(PyAny);
pyobject_native_type!(
PyDateTime,
crate::ffi::PyDateTime_DateTime,
|py| expect_datetime_api(py).DateTimeType,
#module=Some("datetime"),
#checkfunction=PyDateTime_Check
);
impl PyDateTime {
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyDateTime::new` will be replaced by `PyDateTime::new_bound` in a future PyO3 version"
)]
#[allow(clippy::too_many_arguments)]
pub fn new<'py>(
py: Python<'py>,
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
microsecond: u32,
tzinfo: Option<&'py PyTzInfo>,
) -> PyResult<&'py PyDateTime> {
Self::new_bound(
py,
year,
month,
day,
hour,
minute,
second,
microsecond,
tzinfo.map(PyTzInfo::as_borrowed).as_deref(),
)
.map(Bound::into_gil_ref)
}
#[allow(clippy::too_many_arguments)]
pub fn new_bound<'py>(
py: Python<'py>,
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
microsecond: u32,
tzinfo: Option<&Bound<'py, PyTzInfo>>,
) -> PyResult<Bound<'py, PyDateTime>> {
let api = ensure_datetime_api(py)?;
unsafe {
(api.DateTime_FromDateAndTime)(
year,
c_int::from(month),
c_int::from(day),
c_int::from(hour),
c_int::from(minute),
c_int::from(second),
microsecond as c_int,
opt_to_pyobj(tzinfo),
api.DateTimeType,
)
.assume_owned_or_err(py)
.downcast_into_unchecked()
}
}
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyDateTime::new_with_fold` will be replaced by `PyDateTime::new_bound_with_fold` in a future PyO3 version"
)]
#[allow(clippy::too_many_arguments)]
pub fn new_with_fold<'py>(
py: Python<'py>,
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
microsecond: u32,
tzinfo: Option<&'py PyTzInfo>,
fold: bool,
) -> PyResult<&'py PyDateTime> {
Self::new_bound_with_fold(
py,
year,
month,
day,
hour,
minute,
second,
microsecond,
tzinfo.map(PyTzInfo::as_borrowed).as_deref(),
fold,
)
.map(Bound::into_gil_ref)
}
#[allow(clippy::too_many_arguments)]
pub fn new_bound_with_fold<'py>(
py: Python<'py>,
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
microsecond: u32,
tzinfo: Option<&Bound<'py, PyTzInfo>>,
fold: bool,
) -> PyResult<Bound<'py, PyDateTime>> {
let api = ensure_datetime_api(py)?;
unsafe {
(api.DateTime_FromDateAndTimeAndFold)(
year,
c_int::from(month),
c_int::from(day),
c_int::from(hour),
c_int::from(minute),
c_int::from(second),
microsecond as c_int,
opt_to_pyobj(tzinfo),
c_int::from(fold),
api.DateTimeType,
)
.assume_owned_or_err(py)
.downcast_into_unchecked()
}
}
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyDateTime::from_timestamp` will be replaced by `PyDateTime::from_timestamp_bound` in a future PyO3 version"
)]
pub fn from_timestamp<'py>(
py: Python<'py>,
timestamp: f64,
tzinfo: Option<&'py PyTzInfo>,
) -> PyResult<&'py PyDateTime> {
Self::from_timestamp_bound(py, timestamp, tzinfo.map(PyTzInfo::as_borrowed).as_deref())
.map(Bound::into_gil_ref)
}
pub fn from_timestamp_bound<'py>(
py: Python<'py>,
timestamp: f64,
tzinfo: Option<&Bound<'py, PyTzInfo>>,
) -> PyResult<Bound<'py, PyDateTime>> {
let args = IntoPy::<Py<PyTuple>>::into_py((timestamp, tzinfo), py).into_bound(py);
let _api = ensure_datetime_api(py)?;
unsafe {
PyDateTime_FromTimestamp(args.as_ptr())
.assume_owned_or_err(py)
.downcast_into_unchecked()
}
}
}
#[cfg(feature = "gil-refs")]
impl PyDateAccess for PyDateTime {
fn get_year(&self) -> i32 {
self.as_borrowed().get_year()
}
fn get_month(&self) -> u8 {
self.as_borrowed().get_month()
}
fn get_day(&self) -> u8 {
self.as_borrowed().get_day()
}
}
impl PyDateAccess for Bound<'_, PyDateTime> {
fn get_year(&self) -> i32 {
unsafe { PyDateTime_GET_YEAR(self.as_ptr()) }
}
fn get_month(&self) -> u8 {
unsafe { PyDateTime_GET_MONTH(self.as_ptr()) as u8 }
}
fn get_day(&self) -> u8 {
unsafe { PyDateTime_GET_DAY(self.as_ptr()) as u8 }
}
}
#[cfg(feature = "gil-refs")]
impl PyTimeAccess for PyDateTime {
fn get_hour(&self) -> u8 {
self.as_borrowed().get_hour()
}
fn get_minute(&self) -> u8 {
self.as_borrowed().get_minute()
}
fn get_second(&self) -> u8 {
self.as_borrowed().get_second()
}
fn get_microsecond(&self) -> u32 {
self.as_borrowed().get_microsecond()
}
fn get_fold(&self) -> bool {
self.as_borrowed().get_fold()
}
}
impl PyTimeAccess for Bound<'_, PyDateTime> {
fn get_hour(&self) -> u8 {
unsafe { PyDateTime_DATE_GET_HOUR(self.as_ptr()) as u8 }
}
fn get_minute(&self) -> u8 {
unsafe { PyDateTime_DATE_GET_MINUTE(self.as_ptr()) as u8 }
}
fn get_second(&self) -> u8 {
unsafe { PyDateTime_DATE_GET_SECOND(self.as_ptr()) as u8 }
}
fn get_microsecond(&self) -> u32 {
unsafe { PyDateTime_DATE_GET_MICROSECOND(self.as_ptr()) as u32 }
}
fn get_fold(&self) -> bool {
unsafe { PyDateTime_DATE_GET_FOLD(self.as_ptr()) > 0 }
}
}
#[cfg(feature = "gil-refs")]
impl<'py> PyTzInfoAccess<'py> for &'py PyDateTime {
fn get_tzinfo_bound(&self) -> Option<Bound<'py, PyTzInfo>> {
self.as_borrowed().get_tzinfo_bound()
}
}
impl<'py> PyTzInfoAccess<'py> for Bound<'py, PyDateTime> {
fn get_tzinfo_bound(&self) -> Option<Bound<'py, PyTzInfo>> {
let ptr = self.as_ptr() as *mut ffi::PyDateTime_DateTime;
#[cfg(not(GraalPy))]
unsafe {
if (*ptr).hastzinfo != 0 {
Some(
(*ptr)
.tzinfo
.assume_borrowed(self.py())
.to_owned()
.downcast_into_unchecked(),
)
} else {
None
}
}
#[cfg(GraalPy)]
unsafe {
let res = PyDateTime_DATE_GET_TZINFO(ptr as *mut ffi::PyObject);
if Py_IsNone(res) == 1 {
None
} else {
Some(
res.assume_borrowed(self.py())
.to_owned()
.downcast_into_unchecked(),
)
}
}
}
}
#[repr(transparent)]
pub struct PyTime(PyAny);
pyobject_native_type!(
PyTime,
crate::ffi::PyDateTime_Time,
|py| expect_datetime_api(py).TimeType,
#module=Some("datetime"),
#checkfunction=PyTime_Check
);
impl PyTime {
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyTime::new` will be replaced by `PyTime::new_bound` in a future PyO3 version"
)]
pub fn new<'py>(
py: Python<'py>,
hour: u8,
minute: u8,
second: u8,
microsecond: u32,
tzinfo: Option<&'py PyTzInfo>,
) -> PyResult<&'py PyTime> {
Self::new_bound(
py,
hour,
minute,
second,
microsecond,
tzinfo.map(PyTzInfo::as_borrowed).as_deref(),
)
.map(Bound::into_gil_ref)
}
pub fn new_bound<'py>(
py: Python<'py>,
hour: u8,
minute: u8,
second: u8,
microsecond: u32,
tzinfo: Option<&Bound<'py, PyTzInfo>>,
) -> PyResult<Bound<'py, PyTime>> {
let api = ensure_datetime_api(py)?;
unsafe {
(api.Time_FromTime)(
c_int::from(hour),
c_int::from(minute),
c_int::from(second),
microsecond as c_int,
opt_to_pyobj(tzinfo),
api.TimeType,
)
.assume_owned_or_err(py)
.downcast_into_unchecked()
}
}
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyTime::new_with_fold` will be replaced by `PyTime::new_bound_with_fold` in a future PyO3 version"
)]
pub fn new_with_fold<'py>(
py: Python<'py>,
hour: u8,
minute: u8,
second: u8,
microsecond: u32,
tzinfo: Option<&'py PyTzInfo>,
fold: bool,
) -> PyResult<&'py PyTime> {
Self::new_bound_with_fold(
py,
hour,
minute,
second,
microsecond,
tzinfo.map(PyTzInfo::as_borrowed).as_deref(),
fold,
)
.map(Bound::into_gil_ref)
}
pub fn new_bound_with_fold<'py>(
py: Python<'py>,
hour: u8,
minute: u8,
second: u8,
microsecond: u32,
tzinfo: Option<&Bound<'py, PyTzInfo>>,
fold: bool,
) -> PyResult<Bound<'py, PyTime>> {
let api = ensure_datetime_api(py)?;
unsafe {
(api.Time_FromTimeAndFold)(
c_int::from(hour),
c_int::from(minute),
c_int::from(second),
microsecond as c_int,
opt_to_pyobj(tzinfo),
fold as c_int,
api.TimeType,
)
.assume_owned_or_err(py)
.downcast_into_unchecked()
}
}
}
#[cfg(feature = "gil-refs")]
impl PyTimeAccess for PyTime {
fn get_hour(&self) -> u8 {
self.as_borrowed().get_hour()
}
fn get_minute(&self) -> u8 {
self.as_borrowed().get_minute()
}
fn get_second(&self) -> u8 {
self.as_borrowed().get_second()
}
fn get_microsecond(&self) -> u32 {
self.as_borrowed().get_microsecond()
}
fn get_fold(&self) -> bool {
self.as_borrowed().get_fold()
}
}
impl PyTimeAccess for Bound<'_, PyTime> {
fn get_hour(&self) -> u8 {
unsafe { PyDateTime_TIME_GET_HOUR(self.as_ptr()) as u8 }
}
fn get_minute(&self) -> u8 {
unsafe { PyDateTime_TIME_GET_MINUTE(self.as_ptr()) as u8 }
}
fn get_second(&self) -> u8 {
unsafe { PyDateTime_TIME_GET_SECOND(self.as_ptr()) as u8 }
}
fn get_microsecond(&self) -> u32 {
unsafe { PyDateTime_TIME_GET_MICROSECOND(self.as_ptr()) as u32 }
}
fn get_fold(&self) -> bool {
unsafe { PyDateTime_TIME_GET_FOLD(self.as_ptr()) != 0 }
}
}
#[cfg(feature = "gil-refs")]
impl<'py> PyTzInfoAccess<'py> for &'py PyTime {
fn get_tzinfo_bound(&self) -> Option<Bound<'py, PyTzInfo>> {
self.as_borrowed().get_tzinfo_bound()
}
}
impl<'py> PyTzInfoAccess<'py> for Bound<'py, PyTime> {
fn get_tzinfo_bound(&self) -> Option<Bound<'py, PyTzInfo>> {
let ptr = self.as_ptr() as *mut ffi::PyDateTime_Time;
#[cfg(not(GraalPy))]
unsafe {
if (*ptr).hastzinfo != 0 {
Some(
(*ptr)
.tzinfo
.assume_borrowed(self.py())
.to_owned()
.downcast_into_unchecked(),
)
} else {
None
}
}
#[cfg(GraalPy)]
unsafe {
let res = PyDateTime_TIME_GET_TZINFO(ptr as *mut ffi::PyObject);
if Py_IsNone(res) == 1 {
None
} else {
Some(
res.assume_borrowed(self.py())
.to_owned()
.downcast_into_unchecked(),
)
}
}
}
}
#[repr(transparent)]
pub struct PyTzInfo(PyAny);
pyobject_native_type!(
PyTzInfo,
crate::ffi::PyObject,
|py| expect_datetime_api(py).TZInfoType,
#module=Some("datetime"),
#checkfunction=PyTZInfo_Check
);
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`timezone_utc` will be replaced by `timezone_utc_bound` in a future PyO3 version"
)]
pub fn timezone_utc(py: Python<'_>) -> &PyTzInfo {
timezone_utc_bound(py).into_gil_ref()
}
pub fn timezone_utc_bound(py: Python<'_>) -> Bound<'_, PyTzInfo> {
unsafe {
expect_datetime_api(py)
.TimeZone_UTC
.assume_borrowed(py)
.to_owned()
.downcast_into_unchecked()
}
}
#[cfg(feature = "chrono")]
pub(crate) fn timezone_from_offset<'py>(
offset: &Bound<'py, PyDelta>,
) -> PyResult<Bound<'py, PyTzInfo>> {
let py = offset.py();
let api = ensure_datetime_api(py)?;
unsafe {
(api.TimeZone_FromTimeZone)(offset.as_ptr(), ptr::null_mut())
.assume_owned_or_err(py)
.downcast_into_unchecked()
}
}
#[repr(transparent)]
pub struct PyDelta(PyAny);
pyobject_native_type!(
PyDelta,
crate::ffi::PyDateTime_Delta,
|py| expect_datetime_api(py).DeltaType,
#module=Some("datetime"),
#checkfunction=PyDelta_Check
);
impl PyDelta {
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyDelta::new` will be replaced by `PyDelta::new_bound` in a future PyO3 version"
)]
pub fn new(
py: Python<'_>,
days: i32,
seconds: i32,
microseconds: i32,
normalize: bool,
) -> PyResult<&PyDelta> {
Self::new_bound(py, days, seconds, microseconds, normalize).map(Bound::into_gil_ref)
}
pub fn new_bound(
py: Python<'_>,
days: i32,
seconds: i32,
microseconds: i32,
normalize: bool,
) -> PyResult<Bound<'_, PyDelta>> {
let api = ensure_datetime_api(py)?;
unsafe {
(api.Delta_FromDelta)(
days as c_int,
seconds as c_int,
microseconds as c_int,
normalize as c_int,
api.DeltaType,
)
.assume_owned_or_err(py)
.downcast_into_unchecked()
}
}
}
#[cfg(feature = "gil-refs")]
impl PyDeltaAccess for PyDelta {
fn get_days(&self) -> i32 {
self.as_borrowed().get_days()
}
fn get_seconds(&self) -> i32 {
self.as_borrowed().get_seconds()
}
fn get_microseconds(&self) -> i32 {
self.as_borrowed().get_microseconds()
}
}
impl PyDeltaAccess for Bound<'_, PyDelta> {
fn get_days(&self) -> i32 {
unsafe { PyDateTime_DELTA_GET_DAYS(self.as_ptr()) }
}
fn get_seconds(&self) -> i32 {
unsafe { PyDateTime_DELTA_GET_SECONDS(self.as_ptr()) }
}
fn get_microseconds(&self) -> i32 {
unsafe { PyDateTime_DELTA_GET_MICROSECONDS(self.as_ptr()) }
}
}
fn opt_to_pyobj(opt: Option<&Bound<'_, PyTzInfo>>) -> *mut ffi::PyObject {
match opt {
Some(tzi) => tzi.as_ptr(),
None => unsafe { ffi::Py_None() },
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "macros")]
use crate::py_run;
#[test]
#[cfg(feature = "macros")]
#[cfg_attr(target_arch = "wasm32", ignore)] fn test_datetime_fromtimestamp() {
Python::with_gil(|py| {
let dt = PyDateTime::from_timestamp_bound(py, 100.0, None).unwrap();
py_run!(
py,
dt,
"import datetime; assert dt == datetime.datetime.fromtimestamp(100)"
);
let dt =
PyDateTime::from_timestamp_bound(py, 100.0, Some(&timezone_utc_bound(py))).unwrap();
py_run!(
py,
dt,
"import datetime; assert dt == datetime.datetime.fromtimestamp(100, datetime.timezone.utc)"
);
})
}
#[test]
#[cfg(feature = "macros")]
#[cfg_attr(target_arch = "wasm32", ignore)] fn test_date_fromtimestamp() {
Python::with_gil(|py| {
let dt = PyDate::from_timestamp_bound(py, 100).unwrap();
py_run!(
py,
dt,
"import datetime; assert dt == datetime.date.fromtimestamp(100)"
);
})
}
#[test]
#[cfg_attr(target_arch = "wasm32", ignore)] fn test_new_with_fold() {
Python::with_gil(|py| {
let a =
PyDateTime::new_bound_with_fold(py, 2021, 1, 23, 20, 32, 40, 341516, None, false);
let b =
PyDateTime::new_bound_with_fold(py, 2021, 1, 23, 20, 32, 40, 341516, None, true);
assert!(!a.unwrap().get_fold());
assert!(b.unwrap().get_fold());
});
}
#[test]
#[cfg_attr(target_arch = "wasm32", ignore)] fn test_get_tzinfo() {
crate::Python::with_gil(|py| {
let utc = timezone_utc_bound(py);
let dt = PyDateTime::new_bound(py, 2018, 1, 1, 0, 0, 0, 0, Some(&utc)).unwrap();
assert!(dt.get_tzinfo_bound().unwrap().eq(&utc).unwrap());
let dt = PyDateTime::new_bound(py, 2018, 1, 1, 0, 0, 0, 0, None).unwrap();
assert!(dt.get_tzinfo_bound().is_none());
let t = PyTime::new_bound(py, 0, 0, 0, 0, Some(&utc)).unwrap();
assert!(t.get_tzinfo_bound().unwrap().eq(utc).unwrap());
let t = PyTime::new_bound(py, 0, 0, 0, 0, None).unwrap();
assert!(t.get_tzinfo_bound().is_none());
});
}
#[test]
#[cfg(all(feature = "macros", feature = "chrono"))]
#[cfg_attr(target_arch = "wasm32", ignore)] fn test_timezone_from_offset() {
Python::with_gil(|py| {
assert!(
timezone_from_offset(&PyDelta::new_bound(py, 0, -3600, 0, true).unwrap())
.unwrap()
.call_method1("utcoffset", ((),))
.unwrap()
.downcast_into::<PyDelta>()
.unwrap()
.eq(PyDelta::new_bound(py, 0, -3600, 0, true).unwrap())
.unwrap()
);
assert!(
timezone_from_offset(&PyDelta::new_bound(py, 0, 3600, 0, true).unwrap())
.unwrap()
.call_method1("utcoffset", ((),))
.unwrap()
.downcast_into::<PyDelta>()
.unwrap()
.eq(PyDelta::new_bound(py, 0, 3600, 0, true).unwrap())
.unwrap()
);
timezone_from_offset(&PyDelta::new_bound(py, 1, 0, 0, true).unwrap()).unwrap_err();
})
}
}