Merge pull request #3663 from Tpt/chrono-generic-datetime

Chrono: makes FromPyObject impl on DateTime generic
This commit is contained in:
David Hewitt 2023-12-19 08:22:47 +00:00 committed by GitHub
commit 12b44ea16d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 13 additions and 28 deletions

View File

@ -0,0 +1 @@
Implements `FromPyObject` on `chrono::DateTime<Tz>` for all `Tz` and not only `FixedOffset` and `Utc`

View File

@ -44,7 +44,7 @@ use crate::exceptions::{PyTypeError, PyUserWarning, PyValueError};
use crate::types::datetime::timezone_from_offset; use crate::types::datetime::timezone_from_offset;
use crate::types::{ use crate::types::{
timezone_utc, PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess, timezone_utc, PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess,
PyTzInfo, PyTzInfoAccess, PyUnicode, PyTzInfo, PyTzInfoAccess,
}; };
use crate::{FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject}; use crate::{FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject};
use chrono::offset::{FixedOffset, Utc}; use chrono::offset::{FixedOffset, Utc};
@ -200,10 +200,10 @@ impl<Tz: TimeZone> IntoPy<PyObject> for DateTime<Tz> {
} }
} }
impl FromPyObject<'_> for DateTime<FixedOffset> { impl<Tz: TimeZone + for<'a> FromPyObject<'a>> FromPyObject<'_> for DateTime<Tz> {
fn extract(ob: &PyAny) -> PyResult<DateTime<FixedOffset>> { fn extract(ob: &PyAny) -> PyResult<DateTime<Tz>> {
let dt: &PyDateTime = ob.downcast()?; let dt: &PyDateTime = ob.downcast()?;
let tz: FixedOffset = if let Some(tzinfo) = dt.get_tzinfo() { let tz = if let Some(tzinfo) = dt.get_tzinfo() {
tzinfo.extract()? tzinfo.extract()?
} else { } else {
return Err(PyTypeError::new_err( return Err(PyTypeError::new_err(
@ -211,23 +211,12 @@ impl FromPyObject<'_> for DateTime<FixedOffset> {
)); ));
}; };
let dt = NaiveDateTime::new(py_date_to_naive_date(dt)?, py_time_to_naive_time(dt)?); let dt = NaiveDateTime::new(py_date_to_naive_date(dt)?, py_time_to_naive_time(dt)?);
// `FixedOffset` cannot have ambiguities so we don't have to worry about DST folds and such dt.and_local_timezone(tz).single().ok_or_else(|| {
Ok(dt.and_local_timezone(tz).unwrap()) PyValueError::new_err(format!(
} "The datetime {:?} contains an incompatible or ambiguous timezone",
} ob
))
impl FromPyObject<'_> for DateTime<Utc> { })
fn extract(ob: &PyAny) -> PyResult<DateTime<Utc>> {
let dt: &PyDateTime = ob.downcast()?;
let _: Utc = if let Some(tzinfo) = dt.get_tzinfo() {
tzinfo.extract()?
} else {
return Err(PyTypeError::new_err(
"expected a datetime with non-None tzinfo",
));
};
let dt = NaiveDateTime::new(py_date_to_naive_date(dt)?, py_time_to_naive_time(dt)?);
Ok(dt.and_utc())
} }
} }
@ -262,12 +251,7 @@ impl FromPyObject<'_> for FixedOffset {
// Trying to convert None to a PyDelta in the next line will then fail. // Trying to convert None to a PyDelta in the next line will then fail.
let py_timedelta = py_tzinfo.call_method1("utcoffset", (ob.py().None(),))?; let py_timedelta = py_tzinfo.call_method1("utcoffset", (ob.py().None(),))?;
let py_timedelta: &PyDelta = py_timedelta.downcast().map_err(|_| { let py_timedelta: &PyDelta = py_timedelta.downcast().map_err(|_| {
PyTypeError::new_err(format!( PyTypeError::new_err(format!("{:?} is not a fixed offset timezone", py_tzinfo))
"{:?} is not a fixed offset timezone",
py_tzinfo
.repr()
.unwrap_or_else(|_| PyUnicode::new(ob.py(), "repr failed"))
))
})?; })?;
let days = py_timedelta.get_days() as i64; let days = py_timedelta.get_days() as i64;
let seconds = py_timedelta.get_seconds() as i64; let seconds = py_timedelta.get_seconds() as i64;
@ -423,7 +407,7 @@ mod tests {
let res = result.err().unwrap(); let res = result.err().unwrap();
// Also check the error message is what we expect // Also check the error message is what we expect
let msg = res.value(py).repr().unwrap().to_string(); let msg = res.value(py).repr().unwrap().to_string();
assert_eq!(msg, "TypeError('\"zoneinfo.ZoneInfo(key=\\'Europe/London\\')\" is not a fixed offset timezone')"); assert_eq!(msg, "TypeError(\"zoneinfo.ZoneInfo(key='Europe/London') is not a fixed offset timezone\")");
}); });
} }