diff --git a/src/naive/datetime/mod.rs b/src/naive/datetime/mod.rs index 74035a6564..9c4954c62a 100644 --- a/src/naive/datetime/mod.rs +++ b/src/naive/datetime/mod.rs @@ -18,7 +18,7 @@ use crate::format::DelayedFormat; use crate::format::{parse, parse_and_remainder, ParseError, ParseResult, Parsed, StrftimeItems}; use crate::format::{Fixed, Item, Numeric, Pad}; use crate::naive::{Days, IsoWeek, NaiveDate, NaiveTime}; -use crate::offset::Utc; +use crate::offset::{Offset, Utc}; use crate::oldtime::Duration as OldDuration; use crate::{ try_opt, DateTime, Datelike, FixedOffset, LocalResult, Months, TimeZone, Timelike, Weekday, @@ -951,7 +951,8 @@ impl NaiveDateTime { /// ``` #[must_use] pub fn and_local_timezone(&self, tz: Tz) -> LocalResult> { - tz.from_local_datetime(self) + let local_result_offset = tz.offset_from_local_datetime(self); + local_time_min_offset(local_result_offset, self).unwrap_or(LocalResult::None) } /// Converts the `NaiveDateTime` into the timezone-aware `DateTime`. @@ -974,6 +975,25 @@ impl NaiveDateTime { pub const MAX: Self = Self { date: NaiveDate::MAX, time: NaiveTime::MAX }; } +/// Helper function to map `LocalResult` to `LocalResult>`. +/// Returns `None` on out-of-range. +fn local_time_min_offset( + local_result: LocalResult, + local_time: &NaiveDateTime, +) -> Option>> { + Some(match local_result { + LocalResult::None => LocalResult::None, + LocalResult::Single(offset) => LocalResult::Single(DateTime::from_naive_utc_and_offset( + local_time.checked_sub_offset(offset.fix())?, + offset, + )), + LocalResult::Ambiguous(o1, o2) => LocalResult::Ambiguous( + DateTime::from_naive_utc_and_offset(local_time.checked_sub_offset(o1.fix())?, o1), + DateTime::from_naive_utc_and_offset(local_time.checked_sub_offset(o2.fix())?, o2), + ), + }) +} + impl Datelike for NaiveDateTime { /// Returns the year number in the [calendar date](./struct.NaiveDate.html#calendar-date). /// diff --git a/src/naive/datetime/tests.rs b/src/naive/datetime/tests.rs index 7600074bd8..12589bcb37 100644 --- a/src/naive/datetime/tests.rs +++ b/src/naive/datetime/tests.rs @@ -1,7 +1,6 @@ use super::NaiveDateTime; use crate::oldtime::Duration as OldDuration; -use crate::NaiveDate; -use crate::{Datelike, FixedOffset, Utc}; +use crate::{Datelike, FixedOffset, LocalResult, NaiveDate, Utc}; #[test] fn test_datetime_from_timestamp_millis() { @@ -459,3 +458,24 @@ fn test_checked_sub_offset() { // out of range assert!(NaiveDateTime::MAX.checked_sub_offset(negative_offset).is_none()); } + +#[test] +fn test_and_timezone_min_max_dates() { + for offset_hour in -23..=23 { + dbg!(offset_hour); + let offset = FixedOffset::east_opt(offset_hour * 60 * 60).unwrap(); + + let local_max = NaiveDateTime::MAX.and_local_timezone(offset); + if offset_hour >= 0 { + assert_eq!(local_max.unwrap().naive_local(), NaiveDateTime::MAX); + } else { + assert_eq!(local_max, LocalResult::None); + } + let local_min = NaiveDateTime::MIN.and_local_timezone(offset); + if offset_hour <= 0 { + assert_eq!(local_min.unwrap().naive_local(), NaiveDateTime::MIN); + } else { + assert_eq!(local_min, LocalResult::None); + } + } +}