Skip to content

Commit

Permalink
Fix out-of-range panics in methods that use map_local
Browse files Browse the repository at this point in the history
  • Loading branch information
pitdicker committed Jun 8, 2023
1 parent 23868d7 commit 2d317cc
Showing 1 changed file with 35 additions and 1 deletion.
36 changes: 35 additions & 1 deletion src/datetime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,8 @@ fn map_local<Tz: TimeZone, F>(dt: &DateTime<Tz>, mut f: F) -> Option<DateTime<Tz
where
F: FnMut(NaiveDateTime) -> Option<NaiveDateTime>,
{
f(dt.naive_local()).and_then(|datetime| datetime.and_local_timezone(dt.timezone()).single())
f(dt.overflowing_naive_local())
.and_then(|datetime| datetime.and_local_timezone(dt.timezone()).single())
}

impl DateTime<FixedOffset> {
Expand Down Expand Up @@ -940,6 +941,18 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
self.overflowing_naive_local().iso_week()
}

// Note on short-circuiting.
//
// The `with_*` methods have an interesting property: if the local `NaiveDateTime` would be
// out-of-range, there is only exactly one year/month/day/ordinal they can be set to that would
// result in a valid `DateTime`: the one that is already there.
// This is thanks to the restriction that an offset is always less then one day, 24h.
//
// The methods below all end up constructing a new `NaiveDate`, which validates the
// resulting `NaiveDateTime` is in range.
// To prevent failing when the resulting `DateTime` could be in range, all the following
// methods short-circuit when possible.

#[inline]
/// Makes a new `DateTime` with the year number changed, while keeping the same month and day.
///
Expand All @@ -953,6 +966,9 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
/// daylight saving time transition.
fn with_year(&self, year: i32) -> Option<DateTime<Tz>> {
if self.year() == year {
return Some(self.clone()); // See note on short-circuiting above.
}
map_local(self, |datetime| datetime.with_year(year))
}

Expand All @@ -969,6 +985,9 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
/// daylight saving time transition.
#[inline]
fn with_month(&self, month: u32) -> Option<DateTime<Tz>> {
if self.month() == month {
return Some(self.clone()); // See note on short-circuiting above.
}
map_local(self, |datetime| datetime.with_month(month))
}

Expand All @@ -985,6 +1004,9 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
/// daylight saving time transition.
#[inline]
fn with_month0(&self, month0: u32) -> Option<DateTime<Tz>> {
if self.month0() == month0 {
return Some(self.clone()); // See note on short-circuiting above.
}
map_local(self, |datetime| datetime.with_month0(month0))
}

Expand All @@ -1001,6 +1023,9 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
/// daylight saving time transition.
#[inline]
fn with_day(&self, day: u32) -> Option<DateTime<Tz>> {
if self.day() == day {
return Some(self.clone()); // See note on short-circuiting above.
}
map_local(self, |datetime| datetime.with_day(day))
}

Expand All @@ -1017,6 +1042,9 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
/// daylight saving time transition.
#[inline]
fn with_day0(&self, day0: u32) -> Option<DateTime<Tz>> {
if self.day0() == day0 {
return Some(self.clone()); // See note on short-circuiting above.
}
map_local(self, |datetime| datetime.with_day0(day0))
}

Expand All @@ -1033,6 +1061,9 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
/// daylight saving time transition.
#[inline]
fn with_ordinal(&self, ordinal: u32) -> Option<DateTime<Tz>> {
if self.ordinal() == ordinal {
return Some(self.clone()); // See note on short-circuiting above.
}
map_local(self, |datetime| datetime.with_ordinal(ordinal))
}

Expand All @@ -1049,6 +1080,9 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
/// daylight saving time transition.
#[inline]
fn with_ordinal0(&self, ordinal0: u32) -> Option<DateTime<Tz>> {
if self.ordinal0() == ordinal0 {
return Some(self.clone()); // See note on short-circuiting above.
}
map_local(self, |datetime| datetime.with_ordinal0(ordinal0))
}
}
Expand Down

0 comments on commit 2d317cc

Please sign in to comment.