Skip to content

Commit

Permalink
Set correct fold when converting to ambiguous chrono::DateTime<Tz>
Browse files Browse the repository at this point in the history
  • Loading branch information
bschoenmaeckers committed Dec 13, 2024
1 parent 1861f3b commit 043c028
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 3 deletions.
12 changes: 9 additions & 3 deletions src/conversions/chrono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ use crate::types::{
timezone_utc, PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess,
PyTzInfo, PyTzInfoAccess,
};
use crate::{ffi, Bound, FromPyObject, IntoPyObjectExt, PyAny, PyErr, PyObject, PyResult, Python};
use crate::{ffi, Bound, BoundObject, FromPyObject, IntoPyObjectExt, PyAny, PyErr, PyObject, PyResult, Python};
#[cfg(Py_LIMITED_API)]
use crate::{intern, DowncastError};
#[allow(deprecated)]
Expand Down Expand Up @@ -466,14 +466,20 @@ where
truncated_leap_second,
} = (&self.naive_local().time()).into();

let fold = matches!(
self.timezone().offset_from_local_datetime(&self.naive_local()),
LocalResult::Ambiguous(_, latest) if self.offset().fix() == latest.fix()
);

#[cfg(not(Py_LIMITED_API))]
let datetime = PyDateTime::new(py, year, month, day, hour, min, sec, micro, Some(tz))?;
let datetime =
PyDateTime::new_with_fold(py, year, month, day, hour, min, sec, micro, Some(tz), fold)?;

#[cfg(Py_LIMITED_API)]
let datetime = DatetimeTypes::try_get(py).and_then(|dt| {
dt.datetime
.bind(py)
.call1((year, month, day, hour, min, sec, micro, tz))
.call1((year, month, day, hour, min, sec, micro, tz, fold as u8))
})?;

if truncated_leap_second {
Expand Down
37 changes: 37 additions & 0 deletions src/conversions/chrono_tz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ impl FromPyObject<'_> for Tz {
#[cfg(all(test, not(windows)))] // Troubles loading timezones on Windows
mod tests {
use super::*;
use crate::prelude::PyAnyMethods;
use crate::Python;
use chrono::{DateTime, Utc};
use chrono_tz::Tz;

#[test]
fn test_frompyobject() {
Expand All @@ -114,6 +118,39 @@ mod tests {
});
}

#[test]
fn test_ambiguous_datetime_to_pyobject() {
let dates = [
DateTime::<Utc>::from_str("2020-10-24 23:00:00 UTC").unwrap(),
DateTime::<Utc>::from_str("2020-10-25 00:00:00 UTC").unwrap(),
DateTime::<Utc>::from_str("2020-10-25 01:00:00 UTC").unwrap(),
];

let dates = dates.map(|dt| dt.with_timezone(&Tz::Europe__London));

assert_eq!(
dates.map(|dt| dt.to_string()),
[
"2020-10-25 00:00:00 BST",
"2020-10-25 01:00:00 BST",
"2020-10-25 01:00:00 GMT"
]
);

Python::with_gil(|py| {
let pydates = dates.map(|dt| dt.into_pyobject(py).unwrap());
assert_eq!(
pydates.clone().map(|dt| dt.getattr("hour").unwrap().extract::<usize>().unwrap()),
[0, 1, 1]
);

assert_eq!(
pydates.map(|dt| dt.getattr("fold").unwrap().extract::<usize>().unwrap() > 0),
[false, false, true]
);
})
}

#[test]
#[cfg(not(Py_GIL_DISABLED))] // https://github.com/python/cpython/issues/116738#issuecomment-2404360445
fn test_into_pyobject() {
Expand Down

0 comments on commit 043c028

Please sign in to comment.