From bdc574ae92eb9598d9bde881baf7f2fb2b3d1c56 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Fri, 19 May 2023 18:10:01 +0200 Subject: [PATCH] Use `overflowing_naive_local` in `map_local` This fixes out-of-range panics in all the `with_*` methods that use `map_local`. --- src/datetime/mod.rs | 7 ++++++- src/datetime/tests.rs | 48 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index bd17ddec8e..b34416df9a 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -742,7 +742,12 @@ fn map_local(dt: &DateTime, mut f: F) -> Option Option, { - f(dt.naive_local()).and_then(|datetime| dt.timezone().from_local_datetime(&datetime).single()) + f(dt.overflowing_naive_local()) + .and_then(|datetime| dt.timezone().from_local_datetime(&datetime).single()) + .filter(|dt| { + let date = dt.naive_utc().date(); + date >= NaiveDate::MIN && date <= NaiveDate::MAX + }) } impl DateTime { diff --git a/src/datetime/tests.rs b/src/datetime/tests.rs index 2c527b54cc..1b351e22ff 100644 --- a/src/datetime/tests.rs +++ b/src/datetime/tests.rs @@ -1389,6 +1389,54 @@ fn test_min_max_getters() { assert_eq!(beyond_max.nanosecond(), 999_999_999); } +#[test] +fn test_min_max_setters() { + let offset_min = FixedOffset::west_opt(2 * 60 * 60).unwrap(); + let beyond_min = offset_min.from_utc_datetime(&NaiveDateTime::MIN); + let offset_max = FixedOffset::east_opt(2 * 60 * 60).unwrap(); + let beyond_max = offset_max.from_utc_datetime(&NaiveDateTime::MAX); + + assert_eq!(beyond_min.with_year(2020).unwrap().year(), 2020); + assert_eq!(beyond_min.with_month(beyond_min.month()), Some(beyond_min)); + assert_eq!(beyond_min.with_month(3), None); + assert_eq!(beyond_min.with_month0(beyond_min.month0()), Some(beyond_min)); + assert_eq!(beyond_min.with_month0(3), None); + assert_eq!(beyond_min.with_day(beyond_min.day()), Some(beyond_min)); + assert_eq!(beyond_min.with_day(15), None); + assert_eq!(beyond_min.with_day0(beyond_min.day0()), Some(beyond_min)); + assert_eq!(beyond_min.with_day0(15), None); + assert_eq!(beyond_min.with_ordinal(beyond_min.ordinal()), Some(beyond_min)); + assert_eq!(beyond_min.with_ordinal(200), None); + assert_eq!(beyond_min.with_ordinal0(beyond_min.ordinal0()), Some(beyond_min)); + assert_eq!(beyond_min.with_ordinal0(200), None); + assert_eq!(beyond_min.with_hour(beyond_min.hour()), Some(beyond_min)); + assert_eq!(beyond_min.with_hour(23), beyond_min.checked_add_signed(OldDuration::hours(1))); + assert_eq!(beyond_min.with_hour(5), None); + assert_eq!(beyond_min.with_minute(0), Some(beyond_min)); + assert_eq!(beyond_min.with_second(0), Some(beyond_min)); + assert_eq!(beyond_min.with_nanosecond(0), Some(beyond_min)); + + assert_eq!(beyond_max.with_year(2020).unwrap().year(), 2020); + assert_eq!(beyond_max.with_month(beyond_max.month()), Some(beyond_max)); + assert_eq!(beyond_max.with_month(3), None); + assert_eq!(beyond_max.with_month0(beyond_max.month0()), Some(beyond_max)); + assert_eq!(beyond_max.with_month0(3), None); + assert_eq!(beyond_max.with_day(beyond_max.day()), Some(beyond_max)); + assert_eq!(beyond_max.with_day(15), None); + assert_eq!(beyond_max.with_day0(beyond_max.day0()), Some(beyond_max)); + assert_eq!(beyond_max.with_day0(15), None); + assert_eq!(beyond_max.with_ordinal(beyond_max.ordinal()), Some(beyond_max)); + assert_eq!(beyond_max.with_ordinal(200), None); + assert_eq!(beyond_max.with_ordinal0(beyond_max.ordinal0()), Some(beyond_max)); + assert_eq!(beyond_max.with_ordinal0(200), None); + assert_eq!(beyond_max.with_hour(beyond_max.hour()), Some(beyond_max)); + assert_eq!(beyond_max.with_hour(0), beyond_max.checked_sub_signed(OldDuration::hours(1))); + assert_eq!(beyond_max.with_hour(5), None); + assert_eq!(beyond_max.with_minute(beyond_max.minute()), Some(beyond_max)); + assert_eq!(beyond_max.with_second(beyond_max.second()), Some(beyond_max)); + assert_eq!(beyond_max.with_nanosecond(beyond_max.nanosecond()), Some(beyond_max)); +} + #[test] #[should_panic] fn test_local_beyond_min_datetime() {