add `_bound` constructors for datetime types (#3778)
* add `_bound` constructors for datetime types * review: Icxolu feedback * update uses of deprecated timezone_utc
This commit is contained in:
parent
1279467d27
commit
5b9b76fe58
|
@ -7,8 +7,8 @@ use pyo3::types::{
|
||||||
};
|
};
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn make_date(py: Python<'_>, year: i32, month: u8, day: u8) -> PyResult<&PyDate> {
|
fn make_date(py: Python<'_>, year: i32, month: u8, day: u8) -> PyResult<Bound<'_, PyDate>> {
|
||||||
PyDate::new(py, year, month, day)
|
PyDate::new_bound(py, year, month, day)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
|
@ -17,34 +17,34 @@ fn get_date_tuple<'p>(py: Python<'p>, d: &PyDate) -> Bound<'p, PyTuple> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn date_from_timestamp(py: Python<'_>, timestamp: i64) -> PyResult<&PyDate> {
|
fn date_from_timestamp(py: Python<'_>, timestamp: i64) -> PyResult<Bound<'_, PyDate>> {
|
||||||
PyDate::from_timestamp(py, timestamp)
|
PyDate::from_timestamp_bound(py, timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn make_time<'p>(
|
fn make_time<'py>(
|
||||||
py: Python<'p>,
|
py: Python<'py>,
|
||||||
hour: u8,
|
hour: u8,
|
||||||
minute: u8,
|
minute: u8,
|
||||||
second: u8,
|
second: u8,
|
||||||
microsecond: u32,
|
microsecond: u32,
|
||||||
tzinfo: Option<&PyTzInfo>,
|
tzinfo: Option<&Bound<'py, PyTzInfo>>,
|
||||||
) -> PyResult<&'p PyTime> {
|
) -> PyResult<Bound<'py, PyTime>> {
|
||||||
PyTime::new(py, hour, minute, second, microsecond, tzinfo)
|
PyTime::new_bound(py, hour, minute, second, microsecond, tzinfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
#[pyo3(signature = (hour, minute, second, microsecond, tzinfo, fold))]
|
#[pyo3(signature = (hour, minute, second, microsecond, tzinfo, fold))]
|
||||||
fn time_with_fold<'p>(
|
fn time_with_fold<'py>(
|
||||||
py: Python<'p>,
|
py: Python<'py>,
|
||||||
hour: u8,
|
hour: u8,
|
||||||
minute: u8,
|
minute: u8,
|
||||||
second: u8,
|
second: u8,
|
||||||
microsecond: u32,
|
microsecond: u32,
|
||||||
tzinfo: Option<&PyTzInfo>,
|
tzinfo: Option<&Bound<'py, PyTzInfo>>,
|
||||||
fold: bool,
|
fold: bool,
|
||||||
) -> PyResult<&'p PyTime> {
|
) -> PyResult<Bound<'py, PyTime>> {
|
||||||
PyTime::new_with_fold(py, hour, minute, second, microsecond, tzinfo, fold)
|
PyTime::new_bound_with_fold(py, hour, minute, second, microsecond, tzinfo, fold)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
|
@ -75,14 +75,19 @@ fn get_time_tuple_fold<'p>(py: Python<'p>, dt: &PyTime) -> Bound<'p, PyTuple> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn make_delta(py: Python<'_>, days: i32, seconds: i32, microseconds: i32) -> PyResult<&PyDelta> {
|
fn make_delta(
|
||||||
PyDelta::new(py, days, seconds, microseconds, true)
|
py: Python<'_>,
|
||||||
|
days: i32,
|
||||||
|
seconds: i32,
|
||||||
|
microseconds: i32,
|
||||||
|
) -> PyResult<Bound<'_, PyDelta>> {
|
||||||
|
PyDelta::new_bound(py, days, seconds, microseconds, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn get_delta_tuple<'p>(py: Python<'p>, delta: &PyDelta) -> Bound<'p, PyTuple> {
|
fn get_delta_tuple<'py>(delta: &Bound<'py, PyDelta>) -> Bound<'py, PyTuple> {
|
||||||
PyTuple::new_bound(
|
PyTuple::new_bound(
|
||||||
py,
|
delta.py(),
|
||||||
[
|
[
|
||||||
delta.get_days(),
|
delta.get_days(),
|
||||||
delta.get_seconds(),
|
delta.get_seconds(),
|
||||||
|
@ -93,8 +98,8 @@ fn get_delta_tuple<'p>(py: Python<'p>, delta: &PyDelta) -> Bound<'p, PyTuple> {
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn make_datetime<'p>(
|
fn make_datetime<'py>(
|
||||||
py: Python<'p>,
|
py: Python<'py>,
|
||||||
year: i32,
|
year: i32,
|
||||||
month: u8,
|
month: u8,
|
||||||
day: u8,
|
day: u8,
|
||||||
|
@ -102,9 +107,9 @@ fn make_datetime<'p>(
|
||||||
minute: u8,
|
minute: u8,
|
||||||
second: u8,
|
second: u8,
|
||||||
microsecond: u32,
|
microsecond: u32,
|
||||||
tzinfo: Option<&PyTzInfo>,
|
tzinfo: Option<&Bound<'py, PyTzInfo>>,
|
||||||
) -> PyResult<&'p PyDateTime> {
|
) -> PyResult<Bound<'py, PyDateTime>> {
|
||||||
PyDateTime::new(
|
PyDateTime::new_bound(
|
||||||
py,
|
py,
|
||||||
year,
|
year,
|
||||||
month,
|
month,
|
||||||
|
@ -118,7 +123,7 @@ fn make_datetime<'p>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn get_datetime_tuple<'p>(py: Python<'p>, dt: &PyDateTime) -> Bound<'p, PyTuple> {
|
fn get_datetime_tuple<'py>(py: Python<'py>, dt: &Bound<'py, PyDateTime>) -> Bound<'py, PyTuple> {
|
||||||
PyTuple::new_bound(
|
PyTuple::new_bound(
|
||||||
py,
|
py,
|
||||||
[
|
[
|
||||||
|
@ -134,7 +139,10 @@ fn get_datetime_tuple<'p>(py: Python<'p>, dt: &PyDateTime) -> Bound<'p, PyTuple>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn get_datetime_tuple_fold<'p>(py: Python<'p>, dt: &PyDateTime) -> Bound<'p, PyTuple> {
|
fn get_datetime_tuple_fold<'py>(
|
||||||
|
py: Python<'py>,
|
||||||
|
dt: &Bound<'py, PyDateTime>,
|
||||||
|
) -> Bound<'py, PyTuple> {
|
||||||
PyTuple::new_bound(
|
PyTuple::new_bound(
|
||||||
py,
|
py,
|
||||||
[
|
[
|
||||||
|
@ -151,21 +159,21 @@ fn get_datetime_tuple_fold<'p>(py: Python<'p>, dt: &PyDateTime) -> Bound<'p, PyT
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn datetime_from_timestamp<'p>(
|
fn datetime_from_timestamp<'py>(
|
||||||
py: Python<'p>,
|
py: Python<'py>,
|
||||||
ts: f64,
|
ts: f64,
|
||||||
tz: Option<&PyTzInfo>,
|
tz: Option<&Bound<'py, PyTzInfo>>,
|
||||||
) -> PyResult<&'p PyDateTime> {
|
) -> PyResult<Bound<'py, PyDateTime>> {
|
||||||
PyDateTime::from_timestamp(py, ts, tz)
|
PyDateTime::from_timestamp_bound(py, ts, tz)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn get_datetime_tzinfo(dt: &PyDateTime) -> Option<Bound<'_, PyTzInfo>> {
|
fn get_datetime_tzinfo<'py>(dt: &Bound<'py, PyDateTime>) -> Option<Bound<'py, PyTzInfo>> {
|
||||||
dt.get_tzinfo_bound()
|
dt.get_tzinfo_bound()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn get_time_tzinfo(dt: &PyTime) -> Option<Bound<'_, PyTzInfo>> {
|
fn get_time_tzinfo<'py>(dt: &Bound<'py, PyTime>) -> Option<Bound<'py, PyTzInfo>> {
|
||||||
dt.get_tzinfo_bound()
|
dt.get_tzinfo_bound()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,15 +187,19 @@ impl TzClass {
|
||||||
TzClass {}
|
TzClass {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn utcoffset<'p>(&self, py: Python<'p>, _dt: &PyDateTime) -> PyResult<&'p PyDelta> {
|
fn utcoffset<'py>(
|
||||||
PyDelta::new(py, 0, 3600, 0, true)
|
&self,
|
||||||
|
py: Python<'py>,
|
||||||
|
_dt: &Bound<'py, PyDateTime>,
|
||||||
|
) -> PyResult<Bound<'py, PyDelta>> {
|
||||||
|
PyDelta::new_bound(py, 0, 3600, 0, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tzname(&self, _py: Python<'_>, _dt: &PyDateTime) -> String {
|
fn tzname(&self, _dt: &Bound<'_, PyDateTime>) -> String {
|
||||||
String::from("+01:00")
|
String::from("+01:00")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dst(&self, _py: Python<'_>, _dt: &PyDateTime) -> Option<&PyDelta> {
|
fn dst<'py>(&self, _dt: &Bound<'py, PyDateTime>) -> Option<Bound<'py, PyDelta>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,14 +47,12 @@ use crate::types::any::PyAnyMethods;
|
||||||
use crate::types::datetime::timezone_from_offset;
|
use crate::types::datetime::timezone_from_offset;
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
timezone_utc, PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess,
|
timezone_utc_bound, PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime,
|
||||||
PyTzInfo, PyTzInfoAccess,
|
PyTimeAccess, PyTzInfo, PyTzInfoAccess,
|
||||||
};
|
};
|
||||||
#[cfg(Py_LIMITED_API)]
|
#[cfg(Py_LIMITED_API)]
|
||||||
use crate::{intern, DowncastError};
|
use crate::{intern, DowncastError};
|
||||||
use crate::{
|
use crate::{Bound, FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject};
|
||||||
Bound, FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult, Python, ToPyObject,
|
|
||||||
};
|
|
||||||
use chrono::offset::{FixedOffset, Utc};
|
use chrono::offset::{FixedOffset, Utc};
|
||||||
use chrono::{
|
use chrono::{
|
||||||
DateTime, Datelike, Duration, NaiveDate, NaiveDateTime, NaiveTime, Offset, TimeZone, Timelike,
|
DateTime, Datelike, Duration, NaiveDate, NaiveDateTime, NaiveTime, Offset, TimeZone, Timelike,
|
||||||
|
@ -81,7 +79,7 @@ impl ToPyObject for Duration {
|
||||||
// We pass true as the `normalize` parameter since we'd need to do several checks here to
|
// We pass true as the `normalize` parameter since we'd need to do several checks here to
|
||||||
// avoid that, and it shouldn't have a big performance impact.
|
// avoid that, and it shouldn't have a big performance impact.
|
||||||
// The seconds and microseconds cast should never overflow since it's at most the number of seconds per day
|
// The seconds and microseconds cast should never overflow since it's at most the number of seconds per day
|
||||||
PyDelta::new(
|
PyDelta::new_bound(
|
||||||
py,
|
py,
|
||||||
days.try_into().unwrap_or(i32::MAX),
|
days.try_into().unwrap_or(i32::MAX),
|
||||||
secs.try_into().unwrap(),
|
secs.try_into().unwrap(),
|
||||||
|
@ -144,7 +142,7 @@ impl ToPyObject for NaiveDate {
|
||||||
let DateArgs { year, month, day } = self.into();
|
let DateArgs { year, month, day } = self.into();
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
{
|
{
|
||||||
PyDate::new(py, year, month, day)
|
PyDate::new_bound(py, year, month, day)
|
||||||
.expect("failed to construct date")
|
.expect("failed to construct date")
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
@ -189,15 +187,16 @@ impl ToPyObject for NaiveTime {
|
||||||
truncated_leap_second,
|
truncated_leap_second,
|
||||||
} = self.into();
|
} = self.into();
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
let time = PyTime::new(py, hour, min, sec, micro, None).expect("Failed to construct time");
|
let time =
|
||||||
|
PyTime::new_bound(py, hour, min, sec, micro, None).expect("Failed to construct time");
|
||||||
#[cfg(Py_LIMITED_API)]
|
#[cfg(Py_LIMITED_API)]
|
||||||
let time = DatetimeTypes::get(py)
|
let time = DatetimeTypes::get(py)
|
||||||
.time
|
.time
|
||||||
.as_ref(py)
|
.bind(py)
|
||||||
.call1((hour, min, sec, micro))
|
.call1((hour, min, sec, micro))
|
||||||
.expect("failed to construct datetime.time");
|
.expect("failed to construct datetime.time");
|
||||||
if truncated_leap_second {
|
if truncated_leap_second {
|
||||||
warn_truncated_leap_second(time);
|
warn_truncated_leap_second(&time);
|
||||||
}
|
}
|
||||||
time.into()
|
time.into()
|
||||||
}
|
}
|
||||||
|
@ -264,7 +263,7 @@ impl<Tz: TimeZone> ToPyObject for DateTime<Tz> {
|
||||||
// FIXME: convert to better timezone representation here than just convert to fixed offset
|
// FIXME: convert to better timezone representation here than just convert to fixed offset
|
||||||
// See https://github.com/PyO3/pyo3/issues/3266
|
// See https://github.com/PyO3/pyo3/issues/3266
|
||||||
let tz = self.offset().fix().to_object(py);
|
let tz = self.offset().fix().to_object(py);
|
||||||
let tz = tz.downcast(py).unwrap();
|
let tz = tz.bind(py).downcast().unwrap();
|
||||||
naive_datetime_to_py_datetime(py, &self.naive_local(), Some(tz))
|
naive_datetime_to_py_datetime(py, &self.naive_local(), Some(tz))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -310,9 +309,9 @@ impl ToPyObject for FixedOffset {
|
||||||
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
{
|
{
|
||||||
let td = PyDelta::new(py, 0, seconds_offset, 0, true)
|
let td = PyDelta::new_bound(py, 0, seconds_offset, 0, true)
|
||||||
.expect("failed to construct timedelta");
|
.expect("failed to construct timedelta");
|
||||||
timezone_from_offset(py, td)
|
timezone_from_offset(&td)
|
||||||
.expect("Failed to construct PyTimezone")
|
.expect("Failed to construct PyTimezone")
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
@ -366,7 +365,7 @@ impl FromPyObject<'_> for FixedOffset {
|
||||||
|
|
||||||
impl ToPyObject for Utc {
|
impl ToPyObject for Utc {
|
||||||
fn to_object(&self, py: Python<'_>) -> PyObject {
|
fn to_object(&self, py: Python<'_>) -> PyObject {
|
||||||
timezone_utc(py).into()
|
timezone_utc_bound(py).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,7 +377,7 @@ impl IntoPy<PyObject> for Utc {
|
||||||
|
|
||||||
impl FromPyObject<'_> for Utc {
|
impl FromPyObject<'_> for Utc {
|
||||||
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<Utc> {
|
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<Utc> {
|
||||||
let py_utc = timezone_utc(ob.py());
|
let py_utc = timezone_utc_bound(ob.py());
|
||||||
if ob.eq(py_utc)? {
|
if ob.eq(py_utc)? {
|
||||||
Ok(Utc)
|
Ok(Utc)
|
||||||
} else {
|
} else {
|
||||||
|
@ -430,8 +429,8 @@ impl From<&NaiveTime> for TimeArgs {
|
||||||
fn naive_datetime_to_py_datetime(
|
fn naive_datetime_to_py_datetime(
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
naive_datetime: &NaiveDateTime,
|
naive_datetime: &NaiveDateTime,
|
||||||
#[cfg(not(Py_LIMITED_API))] tzinfo: Option<&PyTzInfo>,
|
#[cfg(not(Py_LIMITED_API))] tzinfo: Option<&Bound<'_, PyTzInfo>>,
|
||||||
#[cfg(Py_LIMITED_API)] tzinfo: Option<&PyAny>,
|
#[cfg(Py_LIMITED_API)] tzinfo: Option<&Bound<'_, PyAny>>,
|
||||||
) -> PyObject {
|
) -> PyObject {
|
||||||
let DateArgs { year, month, day } = (&naive_datetime.date()).into();
|
let DateArgs { year, month, day } = (&naive_datetime.date()).into();
|
||||||
let TimeArgs {
|
let TimeArgs {
|
||||||
|
@ -442,21 +441,21 @@ fn naive_datetime_to_py_datetime(
|
||||||
truncated_leap_second,
|
truncated_leap_second,
|
||||||
} = (&naive_datetime.time()).into();
|
} = (&naive_datetime.time()).into();
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
let datetime = PyDateTime::new(py, year, month, day, hour, min, sec, micro, tzinfo)
|
let datetime = PyDateTime::new_bound(py, year, month, day, hour, min, sec, micro, tzinfo)
|
||||||
.expect("failed to construct datetime");
|
.expect("failed to construct datetime");
|
||||||
#[cfg(Py_LIMITED_API)]
|
#[cfg(Py_LIMITED_API)]
|
||||||
let datetime = DatetimeTypes::get(py)
|
let datetime = DatetimeTypes::get(py)
|
||||||
.datetime
|
.datetime
|
||||||
.as_ref(py)
|
.bind(py)
|
||||||
.call1((year, month, day, hour, min, sec, micro, tzinfo))
|
.call1((year, month, day, hour, min, sec, micro, tzinfo))
|
||||||
.expect("failed to construct datetime.datetime");
|
.expect("failed to construct datetime.datetime");
|
||||||
if truncated_leap_second {
|
if truncated_leap_second {
|
||||||
warn_truncated_leap_second(datetime);
|
warn_truncated_leap_second(&datetime);
|
||||||
}
|
}
|
||||||
datetime.into()
|
datetime.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn warn_truncated_leap_second(obj: &PyAny) {
|
fn warn_truncated_leap_second(obj: &Bound<'_, PyAny>) {
|
||||||
let py = obj.py();
|
let py = obj.py();
|
||||||
if let Err(e) = PyErr::warn(
|
if let Err(e) = PyErr::warn(
|
||||||
py,
|
py,
|
||||||
|
@ -558,8 +557,8 @@ impl DatetimeTypes {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(Py_LIMITED_API)]
|
#[cfg(Py_LIMITED_API)]
|
||||||
fn timezone_utc(py: Python<'_>) -> &PyAny {
|
fn timezone_utc_bound(py: Python<'_>) -> Bound<'_, PyAny> {
|
||||||
DatetimeTypes::get(py).timezone_utc.as_ref(py)
|
DatetimeTypes::get(py).timezone_utc.bind(py).clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -913,7 +912,7 @@ mod tests {
|
||||||
let minute = 8;
|
let minute = 8;
|
||||||
let second = 9;
|
let second = 9;
|
||||||
let micro = 999_999;
|
let micro = 999_999;
|
||||||
let tz_utc = timezone_utc(py);
|
let tz_utc = timezone_utc_bound(py);
|
||||||
let py_datetime = new_py_datetime_ob(
|
let py_datetime = new_py_datetime_ob(
|
||||||
py,
|
py,
|
||||||
"datetime",
|
"datetime",
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::types::any::PyAnyMethods;
|
||||||
#[cfg(Py_LIMITED_API)]
|
#[cfg(Py_LIMITED_API)]
|
||||||
use crate::types::PyType;
|
use crate::types::PyType;
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
use crate::types::{timezone_utc, PyDateTime, PyDelta, PyDeltaAccess};
|
use crate::types::{timezone_utc_bound, PyDateTime, PyDelta, PyDeltaAccess};
|
||||||
#[cfg(Py_LIMITED_API)]
|
#[cfg(Py_LIMITED_API)]
|
||||||
use crate::Py;
|
use crate::Py;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -59,7 +59,7 @@ impl ToPyObject for Duration {
|
||||||
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
{
|
{
|
||||||
PyDelta::new(
|
PyDelta::new_bound(
|
||||||
py,
|
py,
|
||||||
days.try_into()
|
days.try_into()
|
||||||
.expect("Too large Rust duration for timedelta"),
|
.expect("Too large Rust duration for timedelta"),
|
||||||
|
@ -130,7 +130,18 @@ fn unix_epoch_py(py: Python<'_>) -> &PyObject {
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
{
|
{
|
||||||
Ok::<_, PyErr>(
|
Ok::<_, PyErr>(
|
||||||
PyDateTime::new(py, 1970, 1, 1, 0, 0, 0, 0, Some(timezone_utc(py)))?.into(),
|
PyDateTime::new_bound(
|
||||||
|
py,
|
||||||
|
1970,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
Some(&timezone_utc_bound(py)),
|
||||||
|
)?
|
||||||
|
.into(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
#[cfg(Py_LIMITED_API)]
|
#[cfg(Py_LIMITED_API)]
|
||||||
|
|
|
@ -77,11 +77,11 @@ fn test_utc_timezone() {
|
||||||
#[cfg(feature = "macros")]
|
#[cfg(feature = "macros")]
|
||||||
#[cfg_attr(target_arch = "wasm32", ignore)] // DateTime import fails on wasm for mysterious reasons
|
#[cfg_attr(target_arch = "wasm32", ignore)] // DateTime import fails on wasm for mysterious reasons
|
||||||
fn test_timezone_from_offset() {
|
fn test_timezone_from_offset() {
|
||||||
use crate::types::PyDelta;
|
use crate::{ffi_ptr_ext::FfiPtrExt, types::PyDelta};
|
||||||
|
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
let delta = PyDelta::new(py, 0, 100, 0, false).unwrap();
|
let delta = PyDelta::new_bound(py, 0, 100, 0, false).unwrap();
|
||||||
let tz: &PyAny = unsafe { py.from_borrowed_ptr(PyTimeZone_FromOffset(delta.as_ptr())) };
|
let tz = unsafe { PyTimeZone_FromOffset(delta.as_ptr()).assume_owned(py) };
|
||||||
crate::py_run!(
|
crate::py_run!(
|
||||||
py,
|
py,
|
||||||
tz,
|
tz,
|
||||||
|
@ -95,16 +95,13 @@ fn test_timezone_from_offset() {
|
||||||
#[cfg(feature = "macros")]
|
#[cfg(feature = "macros")]
|
||||||
#[cfg_attr(target_arch = "wasm32", ignore)] // DateTime import fails on wasm for mysterious reasons
|
#[cfg_attr(target_arch = "wasm32", ignore)] // DateTime import fails on wasm for mysterious reasons
|
||||||
fn test_timezone_from_offset_and_name() {
|
fn test_timezone_from_offset_and_name() {
|
||||||
use crate::types::PyDelta;
|
use crate::{ffi_ptr_ext::FfiPtrExt, types::PyDelta};
|
||||||
|
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
let delta = PyDelta::new(py, 0, 100, 0, false).unwrap();
|
let delta = PyDelta::new_bound(py, 0, 100, 0, false).unwrap();
|
||||||
let tzname = PyString::new_bound(py, "testtz");
|
let tzname = PyString::new_bound(py, "testtz");
|
||||||
let tz: &PyAny = unsafe {
|
let tz = unsafe {
|
||||||
py.from_borrowed_ptr(PyTimeZone_FromOffsetAndName(
|
PyTimeZone_FromOffsetAndName(delta.as_ptr(), tzname.as_ptr()).assume_owned(py)
|
||||||
delta.as_ptr(),
|
|
||||||
tzname.as_ptr(),
|
|
||||||
))
|
|
||||||
};
|
};
|
||||||
crate::py_run!(
|
crate::py_run!(
|
||||||
py,
|
py,
|
||||||
|
@ -253,36 +250,36 @@ fn ucs4() {
|
||||||
#[cfg_attr(target_arch = "wasm32", ignore)] // DateTime import fails on wasm for mysterious reasons
|
#[cfg_attr(target_arch = "wasm32", ignore)] // DateTime import fails on wasm for mysterious reasons
|
||||||
#[cfg(not(PyPy))]
|
#[cfg(not(PyPy))]
|
||||||
fn test_get_tzinfo() {
|
fn test_get_tzinfo() {
|
||||||
use crate::types::timezone_utc;
|
use crate::types::timezone_utc_bound;
|
||||||
|
|
||||||
crate::Python::with_gil(|py| {
|
crate::Python::with_gil(|py| {
|
||||||
use crate::types::{PyDateTime, PyTime};
|
use crate::types::{PyDateTime, PyTime};
|
||||||
use crate::PyAny;
|
use crate::PyAny;
|
||||||
|
|
||||||
let utc = timezone_utc(py);
|
let utc = &timezone_utc_bound(py);
|
||||||
|
|
||||||
let dt = PyDateTime::new(py, 2018, 1, 1, 0, 0, 0, 0, Some(utc)).unwrap();
|
let dt = PyDateTime::new_bound(py, 2018, 1, 1, 0, 0, 0, 0, Some(utc)).unwrap();
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
unsafe { py.from_borrowed_ptr::<PyAny>(PyDateTime_DATE_GET_TZINFO(dt.as_ptr())) }
|
unsafe { py.from_borrowed_ptr::<PyAny>(PyDateTime_DATE_GET_TZINFO(dt.as_ptr())) }
|
||||||
.is(utc)
|
.is(utc)
|
||||||
);
|
);
|
||||||
|
|
||||||
let dt = PyDateTime::new(py, 2018, 1, 1, 0, 0, 0, 0, None).unwrap();
|
let dt = PyDateTime::new_bound(py, 2018, 1, 1, 0, 0, 0, 0, None).unwrap();
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
unsafe { py.from_borrowed_ptr::<PyAny>(PyDateTime_DATE_GET_TZINFO(dt.as_ptr())) }
|
unsafe { py.from_borrowed_ptr::<PyAny>(PyDateTime_DATE_GET_TZINFO(dt.as_ptr())) }
|
||||||
.is_none()
|
.is_none()
|
||||||
);
|
);
|
||||||
|
|
||||||
let t = PyTime::new(py, 0, 0, 0, 0, Some(utc)).unwrap();
|
let t = PyTime::new_bound(py, 0, 0, 0, 0, Some(utc)).unwrap();
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
unsafe { py.from_borrowed_ptr::<PyAny>(PyDateTime_TIME_GET_TZINFO(t.as_ptr())) }
|
unsafe { py.from_borrowed_ptr::<PyAny>(PyDateTime_TIME_GET_TZINFO(t.as_ptr())) }
|
||||||
.is(utc)
|
.is(utc)
|
||||||
);
|
);
|
||||||
|
|
||||||
let t = PyTime::new(py, 0, 0, 0, 0, None).unwrap();
|
let t = PyTime::new_bound(py, 0, 0, 0, 0, None).unwrap();
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
unsafe { py.from_borrowed_ptr::<PyAny>(PyDateTime_TIME_GET_TZINFO(t.as_ptr())) }
|
unsafe { py.from_borrowed_ptr::<PyAny>(PyDateTime_TIME_GET_TZINFO(t.as_ptr())) }
|
||||||
|
|
|
@ -21,6 +21,7 @@ use crate::ffi::{
|
||||||
};
|
};
|
||||||
use crate::ffi_ptr_ext::FfiPtrExt;
|
use crate::ffi_ptr_ext::FfiPtrExt;
|
||||||
use crate::instance::PyNativeType;
|
use crate::instance::PyNativeType;
|
||||||
|
use crate::py_result_ext::PyResultExt;
|
||||||
use crate::types::any::PyAnyMethods;
|
use crate::types::any::PyAnyMethods;
|
||||||
use crate::types::PyTuple;
|
use crate::types::PyTuple;
|
||||||
use crate::{Bound, IntoPy, Py, PyAny, PyErr, Python};
|
use crate::{Bound, IntoPy, Py, PyAny, PyErr, Python};
|
||||||
|
@ -201,27 +202,53 @@ pyobject_native_type!(
|
||||||
);
|
);
|
||||||
|
|
||||||
impl PyDate {
|
impl PyDate {
|
||||||
/// Creates a new `datetime.date`.
|
/// Deprecated form of [`PyDate::new_bound`].
|
||||||
|
#[cfg_attr(
|
||||||
|
not(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> {
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `datetime.date`.
|
||||||
|
pub fn new_bound(py: Python<'_>, year: i32, month: u8, day: u8) -> PyResult<Bound<'_, PyDate>> {
|
||||||
let api = ensure_datetime_api(py)?;
|
let api = ensure_datetime_api(py)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = (api.Date_FromDate)(year, c_int::from(month), c_int::from(day), api.DateType);
|
(api.Date_FromDate)(year, c_int::from(month), c_int::from(day), api.DateType)
|
||||||
py.from_owned_ptr_or_err(ptr)
|
.assume_owned_or_err(py)
|
||||||
|
.downcast_into_unchecked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deprecated form of [`PyDate::from_timestamp_bound`].
|
||||||
|
#[cfg_attr(
|
||||||
|
not(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)
|
||||||
|
}
|
||||||
|
|
||||||
/// Construct a `datetime.date` from a POSIX timestamp
|
/// Construct a `datetime.date` from a POSIX timestamp
|
||||||
///
|
///
|
||||||
/// This is equivalent to `datetime.date.fromtimestamp`
|
/// This is equivalent to `datetime.date.fromtimestamp`
|
||||||
pub fn from_timestamp(py: Python<'_>, timestamp: i64) -> PyResult<&PyDate> {
|
pub fn from_timestamp_bound(py: Python<'_>, timestamp: i64) -> PyResult<Bound<'_, PyDate>> {
|
||||||
let time_tuple = PyTuple::new_bound(py, [timestamp]);
|
let time_tuple = PyTuple::new_bound(py, [timestamp]);
|
||||||
|
|
||||||
// safety ensure that the API is loaded
|
// safety ensure that the API is loaded
|
||||||
let _api = ensure_datetime_api(py)?;
|
let _api = ensure_datetime_api(py)?;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = PyDate_FromTimestamp(time_tuple.as_ptr());
|
PyDate_FromTimestamp(time_tuple.as_ptr())
|
||||||
py.from_owned_ptr_or_err(ptr)
|
.assume_owned_or_err(py)
|
||||||
|
.downcast_into_unchecked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -266,10 +293,17 @@ pyobject_native_type!(
|
||||||
);
|
);
|
||||||
|
|
||||||
impl PyDateTime {
|
impl PyDateTime {
|
||||||
/// Creates a new `datetime.datetime` object.
|
/// Deprecated form of [`PyDateTime::new_bound`].
|
||||||
|
#[cfg_attr(
|
||||||
|
not(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)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new<'p>(
|
pub fn new<'py>(
|
||||||
py: Python<'p>,
|
py: Python<'py>,
|
||||||
year: i32,
|
year: i32,
|
||||||
month: u8,
|
month: u8,
|
||||||
day: u8,
|
day: u8,
|
||||||
|
@ -277,11 +311,38 @@ impl PyDateTime {
|
||||||
minute: u8,
|
minute: u8,
|
||||||
second: u8,
|
second: u8,
|
||||||
microsecond: u32,
|
microsecond: u32,
|
||||||
tzinfo: Option<&PyTzInfo>,
|
tzinfo: Option<&'py PyTzInfo>,
|
||||||
) -> PyResult<&'p PyDateTime> {
|
) -> 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `datetime.datetime` object.
|
||||||
|
#[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)?;
|
let api = ensure_datetime_api(py)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = (api.DateTime_FromDateAndTime)(
|
(api.DateTime_FromDateAndTime)(
|
||||||
year,
|
year,
|
||||||
c_int::from(month),
|
c_int::from(month),
|
||||||
c_int::from(day),
|
c_int::from(day),
|
||||||
|
@ -291,11 +352,48 @@ impl PyDateTime {
|
||||||
microsecond as c_int,
|
microsecond as c_int,
|
||||||
opt_to_pyobj(tzinfo),
|
opt_to_pyobj(tzinfo),
|
||||||
api.DateTimeType,
|
api.DateTimeType,
|
||||||
);
|
)
|
||||||
py.from_owned_ptr_or_err(ptr)
|
.assume_owned_or_err(py)
|
||||||
|
.downcast_into_unchecked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deprecated form of [`PyDateTime::new_bound_with_fold`].
|
||||||
|
#[cfg_attr(
|
||||||
|
not(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)
|
||||||
|
}
|
||||||
|
|
||||||
/// Alternate constructor that takes a `fold` parameter. A `true` value for this parameter
|
/// Alternate constructor that takes a `fold` parameter. A `true` value for this parameter
|
||||||
/// signifies this this datetime is the later of two moments with the same representation,
|
/// signifies this this datetime is the later of two moments with the same representation,
|
||||||
/// during a repeated interval.
|
/// during a repeated interval.
|
||||||
|
@ -304,8 +402,8 @@ impl PyDateTime {
|
||||||
/// represented time is ambiguous.
|
/// represented time is ambiguous.
|
||||||
/// See [PEP 495](https://www.python.org/dev/peps/pep-0495/) for more detail.
|
/// See [PEP 495](https://www.python.org/dev/peps/pep-0495/) for more detail.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new_with_fold<'p>(
|
pub fn new_bound_with_fold<'py>(
|
||||||
py: Python<'p>,
|
py: Python<'py>,
|
||||||
year: i32,
|
year: i32,
|
||||||
month: u8,
|
month: u8,
|
||||||
day: u8,
|
day: u8,
|
||||||
|
@ -313,12 +411,12 @@ impl PyDateTime {
|
||||||
minute: u8,
|
minute: u8,
|
||||||
second: u8,
|
second: u8,
|
||||||
microsecond: u32,
|
microsecond: u32,
|
||||||
tzinfo: Option<&PyTzInfo>,
|
tzinfo: Option<&Bound<'py, PyTzInfo>>,
|
||||||
fold: bool,
|
fold: bool,
|
||||||
) -> PyResult<&'p PyDateTime> {
|
) -> PyResult<Bound<'py, PyDateTime>> {
|
||||||
let api = ensure_datetime_api(py)?;
|
let api = ensure_datetime_api(py)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = (api.DateTime_FromDateAndTimeAndFold)(
|
(api.DateTime_FromDateAndTimeAndFold)(
|
||||||
year,
|
year,
|
||||||
c_int::from(month),
|
c_int::from(month),
|
||||||
c_int::from(day),
|
c_int::from(day),
|
||||||
|
@ -329,27 +427,46 @@ impl PyDateTime {
|
||||||
opt_to_pyobj(tzinfo),
|
opt_to_pyobj(tzinfo),
|
||||||
c_int::from(fold),
|
c_int::from(fold),
|
||||||
api.DateTimeType,
|
api.DateTimeType,
|
||||||
);
|
)
|
||||||
py.from_owned_ptr_or_err(ptr)
|
.assume_owned_or_err(py)
|
||||||
|
.downcast_into_unchecked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deprecated form of [`PyDateTime::from_timestamp_bound`].
|
||||||
|
#[cfg_attr(
|
||||||
|
not(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)
|
||||||
|
}
|
||||||
|
|
||||||
/// Construct a `datetime` object from a POSIX timestamp
|
/// Construct a `datetime` object from a POSIX timestamp
|
||||||
///
|
///
|
||||||
/// This is equivalent to `datetime.datetime.fromtimestamp`
|
/// This is equivalent to `datetime.datetime.fromtimestamp`
|
||||||
pub fn from_timestamp<'p>(
|
pub fn from_timestamp_bound<'py>(
|
||||||
py: Python<'p>,
|
py: Python<'py>,
|
||||||
timestamp: f64,
|
timestamp: f64,
|
||||||
tzinfo: Option<&PyTzInfo>,
|
tzinfo: Option<&Bound<'py, PyTzInfo>>,
|
||||||
) -> PyResult<&'p PyDateTime> {
|
) -> PyResult<Bound<'py, PyDateTime>> {
|
||||||
let args: Py<PyTuple> = (timestamp, tzinfo).into_py(py);
|
let args = IntoPy::<Py<PyTuple>>::into_py((timestamp, tzinfo), py).into_bound(py);
|
||||||
|
|
||||||
// safety ensure API is loaded
|
// safety ensure API is loaded
|
||||||
let _api = ensure_datetime_api(py)?;
|
let _api = ensure_datetime_api(py)?;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = PyDateTime_FromTimestamp(args.as_ptr());
|
PyDateTime_FromTimestamp(args.as_ptr())
|
||||||
py.from_owned_ptr_or_err(ptr)
|
.assume_owned_or_err(py)
|
||||||
|
.downcast_into_unchecked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -463,42 +580,99 @@ pyobject_native_type!(
|
||||||
);
|
);
|
||||||
|
|
||||||
impl PyTime {
|
impl PyTime {
|
||||||
/// Creates a new `datetime.time` object.
|
/// Deprecated form of [`PyTime::new_bound`].
|
||||||
pub fn new<'p>(
|
#[cfg_attr(
|
||||||
py: Python<'p>,
|
not(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,
|
hour: u8,
|
||||||
minute: u8,
|
minute: u8,
|
||||||
second: u8,
|
second: u8,
|
||||||
microsecond: u32,
|
microsecond: u32,
|
||||||
tzinfo: Option<&PyTzInfo>,
|
tzinfo: Option<&'py PyTzInfo>,
|
||||||
) -> PyResult<&'p PyTime> {
|
) -> PyResult<&'py PyTime> {
|
||||||
|
Self::new_bound(
|
||||||
|
py,
|
||||||
|
hour,
|
||||||
|
minute,
|
||||||
|
second,
|
||||||
|
microsecond,
|
||||||
|
tzinfo.map(PyTzInfo::as_borrowed).as_deref(),
|
||||||
|
)
|
||||||
|
.map(Bound::into_gil_ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `datetime.time` object.
|
||||||
|
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)?;
|
let api = ensure_datetime_api(py)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = (api.Time_FromTime)(
|
(api.Time_FromTime)(
|
||||||
c_int::from(hour),
|
c_int::from(hour),
|
||||||
c_int::from(minute),
|
c_int::from(minute),
|
||||||
c_int::from(second),
|
c_int::from(second),
|
||||||
microsecond as c_int,
|
microsecond as c_int,
|
||||||
opt_to_pyobj(tzinfo),
|
opt_to_pyobj(tzinfo),
|
||||||
api.TimeType,
|
api.TimeType,
|
||||||
);
|
)
|
||||||
py.from_owned_ptr_or_err(ptr)
|
.assume_owned_or_err(py)
|
||||||
|
.downcast_into_unchecked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Alternate constructor that takes a `fold` argument. See [`PyDateTime::new_with_fold`].
|
/// Deprecated form of [`PyTime::new_bound_with_fold`].
|
||||||
pub fn new_with_fold<'p>(
|
#[cfg_attr(
|
||||||
py: Python<'p>,
|
not(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,
|
hour: u8,
|
||||||
minute: u8,
|
minute: u8,
|
||||||
second: u8,
|
second: u8,
|
||||||
microsecond: u32,
|
microsecond: u32,
|
||||||
tzinfo: Option<&PyTzInfo>,
|
tzinfo: Option<&'py PyTzInfo>,
|
||||||
fold: bool,
|
fold: bool,
|
||||||
) -> PyResult<&'p PyTime> {
|
) -> 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Alternate constructor that takes a `fold` argument. See [`PyDateTime::new_with_fold`].
|
||||||
|
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)?;
|
let api = ensure_datetime_api(py)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = (api.Time_FromTimeAndFold)(
|
(api.Time_FromTimeAndFold)(
|
||||||
c_int::from(hour),
|
c_int::from(hour),
|
||||||
c_int::from(minute),
|
c_int::from(minute),
|
||||||
c_int::from(second),
|
c_int::from(second),
|
||||||
|
@ -506,8 +680,9 @@ impl PyTime {
|
||||||
opt_to_pyobj(tzinfo),
|
opt_to_pyobj(tzinfo),
|
||||||
fold as c_int,
|
fold as c_int,
|
||||||
api.TimeType,
|
api.TimeType,
|
||||||
);
|
)
|
||||||
py.from_owned_ptr_or_err(ptr)
|
.assume_owned_or_err(py)
|
||||||
|
.downcast_into_unchecked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -596,20 +771,45 @@ pyobject_native_type!(
|
||||||
#checkfunction=PyTZInfo_Check
|
#checkfunction=PyTZInfo_Check
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Equivalent to `datetime.timezone.utc`
|
/// Deprecated form of [`timezone_utc_bound`].
|
||||||
|
#[cfg_attr(
|
||||||
|
not(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 {
|
pub fn timezone_utc(py: Python<'_>) -> &PyTzInfo {
|
||||||
unsafe { &*(expect_datetime_api(py).TimeZone_UTC as *const PyTzInfo) }
|
timezone_utc_bound(py).into_gil_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Equivalent to `datetime.timezone.utc`
|
||||||
|
pub fn timezone_utc_bound(py: Python<'_>) -> Bound<'_, PyTzInfo> {
|
||||||
|
// TODO: this _could_ have a borrowed form `timezone_utc_borrowed`, but that seems
|
||||||
|
// like an edge case optimization and we'd prefer in PyO3 0.21 to use `Bound` as
|
||||||
|
// much as possible
|
||||||
|
unsafe {
|
||||||
|
expect_datetime_api(py)
|
||||||
|
.TimeZone_UTC
|
||||||
|
.assume_borrowed(py)
|
||||||
|
.to_owned()
|
||||||
|
.downcast_into_unchecked()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Equivalent to `datetime.timezone` constructor
|
/// Equivalent to `datetime.timezone` constructor
|
||||||
///
|
///
|
||||||
/// Only used internally
|
/// Only used internally
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
pub fn timezone_from_offset<'a>(py: Python<'a>, offset: &PyDelta) -> PyResult<&'a PyTzInfo> {
|
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)?;
|
let api = ensure_datetime_api(py)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = (api.TimeZone_FromTimeZone)(offset.as_ptr(), ptr::null_mut());
|
(api.TimeZone_FromTimeZone)(offset.as_ptr(), ptr::null_mut())
|
||||||
py.from_owned_ptr_or_err(ptr)
|
.assume_owned_or_err(py)
|
||||||
|
.downcast_into_unchecked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -625,7 +825,14 @@ pyobject_native_type!(
|
||||||
);
|
);
|
||||||
|
|
||||||
impl PyDelta {
|
impl PyDelta {
|
||||||
/// Creates a new `timedelta`.
|
/// Deprecated form of [`PyDelta::new_bound`].
|
||||||
|
#[cfg_attr(
|
||||||
|
not(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(
|
pub fn new(
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
days: i32,
|
days: i32,
|
||||||
|
@ -633,16 +840,28 @@ impl PyDelta {
|
||||||
microseconds: i32,
|
microseconds: i32,
|
||||||
normalize: bool,
|
normalize: bool,
|
||||||
) -> PyResult<&PyDelta> {
|
) -> PyResult<&PyDelta> {
|
||||||
|
Self::new_bound(py, days, seconds, microseconds, normalize).map(Bound::into_gil_ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `timedelta`.
|
||||||
|
pub fn new_bound(
|
||||||
|
py: Python<'_>,
|
||||||
|
days: i32,
|
||||||
|
seconds: i32,
|
||||||
|
microseconds: i32,
|
||||||
|
normalize: bool,
|
||||||
|
) -> PyResult<Bound<'_, PyDelta>> {
|
||||||
let api = ensure_datetime_api(py)?;
|
let api = ensure_datetime_api(py)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = (api.Delta_FromDelta)(
|
(api.Delta_FromDelta)(
|
||||||
days as c_int,
|
days as c_int,
|
||||||
seconds as c_int,
|
seconds as c_int,
|
||||||
microseconds as c_int,
|
microseconds as c_int,
|
||||||
normalize as c_int,
|
normalize as c_int,
|
||||||
api.DeltaType,
|
api.DeltaType,
|
||||||
);
|
)
|
||||||
py.from_owned_ptr_or_err(ptr)
|
.assume_owned_or_err(py)
|
||||||
|
.downcast_into_unchecked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -677,7 +896,7 @@ impl PyDeltaAccess for Bound<'_, PyDelta> {
|
||||||
|
|
||||||
// Utility function which returns a borrowed reference to either
|
// Utility function which returns a borrowed reference to either
|
||||||
// the underlying tzinfo or None.
|
// the underlying tzinfo or None.
|
||||||
fn opt_to_pyobj(opt: Option<&PyTzInfo>) -> *mut ffi::PyObject {
|
fn opt_to_pyobj(opt: Option<&Bound<'_, PyTzInfo>>) -> *mut ffi::PyObject {
|
||||||
match opt {
|
match opt {
|
||||||
Some(tzi) => tzi.as_ptr(),
|
Some(tzi) => tzi.as_ptr(),
|
||||||
None => unsafe { ffi::Py_None() },
|
None => unsafe { ffi::Py_None() },
|
||||||
|
@ -685,6 +904,7 @@ fn opt_to_pyobj(opt: Option<&PyTzInfo>) -> *mut ffi::PyObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[cfg(feature = "macros")]
|
#[cfg(feature = "macros")]
|
||||||
|
@ -768,7 +988,7 @@ mod tests {
|
||||||
fn test_timezone_from_offset() {
|
fn test_timezone_from_offset() {
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
assert!(
|
assert!(
|
||||||
timezone_from_offset(py, PyDelta::new(py, 0, -3600, 0, true).unwrap())
|
timezone_from_offset(&PyDelta::new_bound(py, 0, -3600, 0, true).unwrap())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.call_method1("utcoffset", ((),))
|
.call_method1("utcoffset", ((),))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -779,7 +999,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
timezone_from_offset(py, PyDelta::new(py, 0, 3600, 0, true).unwrap())
|
timezone_from_offset(&PyDelta::new_bound(py, 0, 3600, 0, true).unwrap())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.call_method1("utcoffset", ((),))
|
.call_method1("utcoffset", ((),))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -789,7 +1009,7 @@ mod tests {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
timezone_from_offset(py, PyDelta::new(py, 1, 0, 0, true).unwrap()).unwrap_err();
|
timezone_from_offset(&PyDelta::new_bound(py, 1, 0, 0, true).unwrap()).unwrap_err();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,13 @@ pub use self::capsule::PyCapsule;
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
pub use self::code::PyCode;
|
pub use self::code::PyCode;
|
||||||
pub use self::complex::PyComplex;
|
pub use self::complex::PyComplex;
|
||||||
|
#[allow(deprecated)]
|
||||||
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
|
pub use self::datetime::timezone_utc;
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
pub use self::datetime::{
|
pub use self::datetime::{
|
||||||
timezone_utc, PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess,
|
timezone_utc_bound, PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime,
|
||||||
PyTzInfo, PyTzInfoAccess,
|
PyTimeAccess, PyTzInfo, PyTzInfoAccess,
|
||||||
};
|
};
|
||||||
pub use self::dict::{IntoPyDict, PyDict};
|
pub use self::dict::{IntoPyDict, PyDict};
|
||||||
#[cfg(not(PyPy))]
|
#[cfg(not(PyPy))]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#![cfg(not(Py_LIMITED_API))]
|
#![cfg(not(Py_LIMITED_API))]
|
||||||
|
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use pyo3::types::{timezone_utc, IntoPyDict, PyDate, PyDateTime, PyTime};
|
use pyo3::types::{timezone_utc_bound, IntoPyDict, PyDate, PyDateTime, PyTime};
|
||||||
use pyo3_ffi::PyDateTime_IMPORT;
|
use pyo3_ffi::PyDateTime_IMPORT;
|
||||||
|
|
||||||
fn _get_subclasses<'py>(
|
fn _get_subclasses<'py>(
|
||||||
|
@ -118,9 +118,9 @@ fn test_datetime_utc() {
|
||||||
use pyo3::types::PyDateTime;
|
use pyo3::types::PyDateTime;
|
||||||
|
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
let utc = timezone_utc(py);
|
let utc = timezone_utc_bound(py);
|
||||||
|
|
||||||
let dt = PyDateTime::new(py, 2018, 1, 1, 0, 0, 0, 0, Some(utc)).unwrap();
|
let dt = PyDateTime::new_bound(py, 2018, 1, 1, 0, 0, 0, 0, Some(&utc)).unwrap();
|
||||||
|
|
||||||
let locals = [("dt", dt)].into_py_dict_bound(py);
|
let locals = [("dt", dt)].into_py_dict_bound(py);
|
||||||
|
|
||||||
|
@ -155,7 +155,7 @@ fn test_pydate_out_of_bounds() {
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
for val in INVALID_DATES {
|
for val in INVALID_DATES {
|
||||||
let (year, month, day) = val;
|
let (year, month, day) = val;
|
||||||
let dt = PyDate::new(py, *year, *month, *day);
|
let dt = PyDate::new_bound(py, *year, *month, *day);
|
||||||
dt.unwrap_err();
|
dt.unwrap_err();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -168,7 +168,7 @@ fn test_pytime_out_of_bounds() {
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
for val in INVALID_TIMES {
|
for val in INVALID_TIMES {
|
||||||
let (hour, minute, second, microsecond) = val;
|
let (hour, minute, second, microsecond) = val;
|
||||||
let dt = PyTime::new(py, *hour, *minute, *second, *microsecond, None);
|
let dt = PyTime::new_bound(py, *hour, *minute, *second, *microsecond, None);
|
||||||
dt.unwrap_err();
|
dt.unwrap_err();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -192,7 +192,7 @@ fn test_pydatetime_out_of_bounds() {
|
||||||
let (date, time) = val;
|
let (date, time) = val;
|
||||||
let (year, month, day) = date;
|
let (year, month, day) = date;
|
||||||
let (hour, minute, second, microsecond) = time;
|
let (hour, minute, second, microsecond) = time;
|
||||||
let dt = PyDateTime::new(
|
let dt = PyDateTime::new_bound(
|
||||||
py,
|
py,
|
||||||
*year,
|
*year,
|
||||||
*month,
|
*month,
|
||||||
|
|
|
@ -21,6 +21,6 @@ fn test_bad_datetime_module_panic() {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// This should panic because the "datetime" module is empty
|
// This should panic because the "datetime" module is empty
|
||||||
PyDate::new(py, 2018, 1, 1).unwrap();
|
PyDate::new_bound(py, 2018, 1, 1).unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue