mirror of
https://github.com/chronotope/chrono.git
synced 2025-09-30 22:42:08 +00:00
Fix panic in default impl of TimeZone::from_local_datetime
This commit is contained in:
parent
652bd7842a
commit
cf62e4648c
@ -1,7 +1,6 @@
|
||||
use super::NaiveDateTime;
|
||||
use crate::duration::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() {
|
||||
@ -490,3 +489,24 @@ fn test_checked_sub_offset() {
|
||||
assert_eq!(dt.checked_add_offset(positive_offset), Some(dt + positive_offset));
|
||||
assert_eq!(dt.checked_sub_offset(positive_offset), Some(dt - positive_offset));
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -500,8 +500,24 @@ pub trait TimeZone: Sized + Clone {
|
||||
/// Converts the local `NaiveDateTime` to the timezone-aware `DateTime` if possible.
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Self>> {
|
||||
self.offset_from_local_datetime(local)
|
||||
.map(|offset| DateTime::from_naive_utc_and_offset(*local - offset.fix(), offset))
|
||||
// Return `LocalResult::None` when the offset pushes a value out of range, instead of
|
||||
// panicking.
|
||||
match self.offset_from_local_datetime(local) {
|
||||
LocalResult::None => LocalResult::None,
|
||||
LocalResult::Single(offset) => match local.checked_sub_offset(offset.fix()) {
|
||||
Some(dt) => LocalResult::Single(DateTime::from_naive_utc_and_offset(dt, offset)),
|
||||
None => LocalResult::None,
|
||||
},
|
||||
LocalResult::Ambiguous(o1, o2) => {
|
||||
match (local.checked_sub_offset(o1.fix()), local.checked_sub_offset(o2.fix())) {
|
||||
(Some(d1), Some(d2)) => LocalResult::Ambiguous(
|
||||
DateTime::from_naive_utc_and_offset(d1, o1),
|
||||
DateTime::from_naive_utc_and_offset(d2, o2),
|
||||
),
|
||||
_ => LocalResult::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates the offset for given UTC `NaiveDate`. This cannot fail.
|
||||
@ -531,6 +547,32 @@ pub trait TimeZone: Sized + Clone {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_fixed_offset_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 = offset.from_utc_datetime(&NaiveDateTime::MAX);
|
||||
assert_eq!(local_max.naive_utc(), NaiveDateTime::MAX);
|
||||
let local_min = offset.from_utc_datetime(&NaiveDateTime::MIN);
|
||||
assert_eq!(local_min.naive_utc(), NaiveDateTime::MIN);
|
||||
|
||||
let local_max = offset.from_local_datetime(&NaiveDateTime::MAX);
|
||||
if offset_hour >= 0 {
|
||||
assert_eq!(local_max.unwrap().naive_local(), NaiveDateTime::MAX);
|
||||
} else {
|
||||
assert_eq!(local_max, LocalResult::None);
|
||||
}
|
||||
let local_min = offset.from_local_datetime(&NaiveDateTime::MIN);
|
||||
if offset_hour <= 0 {
|
||||
assert_eq!(local_min.unwrap().naive_local(), NaiveDateTime::MIN);
|
||||
} else {
|
||||
assert_eq!(local_min, LocalResult::None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negative_millis() {
|
||||
let dt = Utc.timestamp_millis_opt(-1000).unwrap();
|
||||
|
Loading…
x
Reference in New Issue
Block a user