From d9045340bf4f375da4d4c608a6bb2f4d84cc9cec Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Fri, 5 May 2023 06:17:14 +0200 Subject: [PATCH] Add tests for datetimes where the local value is out of range --- src/datetime/tests.rs | 153 +++++++++++++++++++++++++++++++++++++++++- src/naive/time/mod.rs | 2 +- 2 files changed, 153 insertions(+), 2 deletions(-) diff --git a/src/datetime/tests.rs b/src/datetime/tests.rs index cf24cb4963..1159105910 100644 --- a/src/datetime/tests.rs +++ b/src/datetime/tests.rs @@ -4,7 +4,7 @@ use crate::naive::{NaiveDate, NaiveTime}; use crate::offset::{FixedOffset, TimeZone, Utc}; #[cfg(feature = "clock")] use crate::offset::{Local, Offset}; -use crate::{Datelike, Days, LocalResult, Months, NaiveDateTime, Timelike}; +use crate::{Datelike, Days, LocalResult, Months, NaiveDateTime, Timelike, Weekday}; #[derive(Clone)] struct DstTester; @@ -1327,6 +1327,157 @@ fn test_datetime_sub_assign() { assert_eq!(datetime_sub, datetime - OldDuration::minutes(90)); } +#[test] +fn test_min_max_datetimes() { + let offset_min = FixedOffset::west_opt(2 * 60 * 60).unwrap(); + let min = offset_min.from_utc_datetime(&NaiveDateTime::MIN); + let offset_max = FixedOffset::east_opt(2 * 60 * 60).unwrap(); + let max = offset_max.from_utc_datetime(&NaiveDateTime::MAX); + + assert_eq!(format!("{:?}", min), "-262144-12-31T22:00:00-02:00"); + // RFC 2822 doesn't support years with more than 4 digits. + // assert_eq!(min.to_rfc2822(), ""); + #[cfg(any(feature = "alloc", feature = "std"))] + assert_eq!(min.to_rfc3339(), "-262144-12-31T22:00:00-02:00"); + #[cfg(any(feature = "alloc", feature = "std"))] + assert_eq!(min.format("%Y-%m-%dT%H:%M:%S%:z").to_string(), "-262144-12-31T22:00:00-02:00"); + assert_eq!(min.year(), -262144); + assert_eq!(min.month(), 12); + assert_eq!(min.month0(), 11); + assert_eq!(min.day(), 31); + assert_eq!(min.day0(), 30); + assert_eq!(min.ordinal(), 366); + assert_eq!(min.ordinal0(), 365); + assert_eq!(min.weekday(), Weekday::Wed); + assert_eq!(min.iso_week().year(), -262143); + assert_eq!(min.iso_week().week(), 1); + assert_eq!(min.checked_add_days(Days::new(0)), None); + assert_eq!( + min.checked_add_days(Days::new(1)), + Some(offset_min.from_utc_datetime(&(NaiveDate::MIN + Days(1)).and_time(NaiveTime::MIN))) + ); + assert_eq!(min.checked_sub_days(Days::new(0)), None); + assert_eq!(min.checked_sub_days(Days::new(1)), None); + assert_eq!(min.checked_add_months(Months::new(0)), Some(min)); + assert_eq!( + min.checked_add_months(Months::new(1)), + Some(offset_min.from_utc_datetime(&(NaiveDate::MIN + Months(1)).and_time(NaiveTime::MIN))) + ); + assert_eq!(min.checked_sub_months(Months::new(0)), Some(min)); + assert_eq!(min.checked_sub_months(Months::new(1)), None); + assert_eq!(min.with_year(min.year()), Some(min)); + assert_eq!( + min.with_year(2020), + offset_min + .from_local_datetime( + &(NaiveDate::MIN.with_year(2021).unwrap().and_time(NaiveTime::MIN) + offset_min) + ) + .single() + ); + assert_eq!(min.with_month(min.month()), Some(min)); + assert_eq!(min.with_month(3), None); + assert_eq!(min.with_month0(min.month0()), Some(min)); + assert_eq!(min.with_month0(3), None); + assert_eq!(min.with_day(min.day()), Some(min)); + assert_eq!(min.with_day(15), None); + assert_eq!(min.with_day0(min.day0()), Some(min)); + assert_eq!(min.with_day0(15), None); + assert_eq!(min.with_ordinal(min.ordinal()), Some(min)); + assert_eq!(min.with_ordinal(200), None); + assert_eq!(min.with_ordinal0(min.ordinal0()), Some(min)); + assert_eq!(min.with_ordinal0(200), None); + assert_eq!(min.hour(), 22); + assert_eq!(min.minute(), 0); + assert_eq!(min.second(), 0); + assert_eq!(min.nanosecond(), 0); + assert_eq!(min.with_hour(min.hour()), Some(min)); + assert_eq!(min.with_hour(23), min.checked_add_signed(OldDuration::hours(1))); + assert_eq!(min.with_hour(5), None); + assert_eq!(min.with_minute(0), Some(min)); + assert_eq!(min.with_second(0), Some(min)); + assert_eq!(min.with_nanosecond(0), Some(min)); + + assert_eq!(format!("{:?}", max), "+262143-01-01T01:59:59.999999999+02:00"); + // RFC 2822 doesn't support years with more than 4 digits. + // assert_eq!(min.to_rfc2822(), ""); + #[cfg(any(feature = "alloc", feature = "std"))] + assert_eq!(max.to_rfc3339(), "+262143-01-01T01:59:59.999999999+02:00"); + #[cfg(any(feature = "alloc", feature = "std"))] + assert_eq!( + max.format("%Y-%m-%dT%H:%M:%S%.9f%:z").to_string(), + "+262143-01-01T01:59:59.999999999+02:00" + ); + assert_eq!(max.year(), 262143); + assert_eq!(max.month(), 1); + assert_eq!(max.month0(), 0); + assert_eq!(max.day(), 1); + assert_eq!(max.day0(), 0); + assert_eq!(max.ordinal(), 1); + assert_eq!(max.ordinal0(), 0); + assert_eq!(max.weekday(), Weekday::Tue); + assert_eq!(max.iso_week().year(), 262143); + assert_eq!(max.iso_week().week(), 1); + assert_eq!(max.checked_add_days(Days::new(0)), None); + assert_eq!(max.checked_add_days(Days::new(1)), None); + assert_eq!(max.checked_sub_days(Days::new(0)), None); + assert_eq!( + max.checked_sub_days(Days::new(1)), + Some(offset_max.from_utc_datetime(&(NaiveDate::MAX - Days(1)).and_time(NaiveTime::MAX))) + ); + assert_eq!(max.checked_add_months(Months::new(0)), Some(max)); + assert_eq!(max.checked_add_months(Months::new(1)), None); + assert_eq!(max.checked_sub_months(Months::new(0)), Some(max)); + assert_eq!( + max.checked_sub_months(Months::new(1)), + Some(offset_max.from_utc_datetime(&(NaiveDate::MAX - Months(1)).and_time(NaiveTime::MAX))) + ); + assert_eq!(max.with_year(max.year()), Some(max)); + assert_eq!( + max.with_year(2020), + offset_max + .from_local_datetime( + &(NaiveDate::MAX.with_year(2019).unwrap().and_time(NaiveTime::MAX) + offset_max) + ) + .single() + ); + assert_eq!(max.with_month(max.month()), Some(max)); + assert_eq!(max.with_month(3), None); + assert_eq!(max.with_month0(max.month0()), Some(max)); + assert_eq!(max.with_month0(3), None); + assert_eq!(max.with_day(max.day()), Some(max)); + assert_eq!(max.with_day(15), None); + assert_eq!(max.with_day0(max.day0()), Some(max)); + assert_eq!(max.with_day0(15), None); + assert_eq!(max.with_ordinal(max.ordinal()), Some(max)); + assert_eq!(max.with_ordinal(200), None); + assert_eq!(max.with_ordinal0(max.ordinal0()), Some(max)); + assert_eq!(max.with_ordinal0(200), None); + assert_eq!(max.hour(), 1); + assert_eq!(max.minute(), 59); + assert_eq!(max.second(), 59); + assert_eq!(max.nanosecond(), 999_999_999); + assert_eq!(max.with_hour(max.hour()), Some(max)); + assert_eq!(max.with_hour(0), max.checked_sub_signed(OldDuration::hours(1))); + assert_eq!(max.with_hour(5), None); + assert_eq!(max.with_minute(max.minute()), Some(max)); + assert_eq!(max.with_second(max.second()), Some(max)); + assert_eq!(max.with_nanosecond(max.nanosecond()), Some(max)); +} + +#[test] +#[should_panic] +fn test_local_beyond_min_datetime() { + let min = FixedOffset::west_opt(2 * 60 * 60).unwrap().from_utc_datetime(&NaiveDateTime::MIN); + let _ = min.naive_local(); +} + +#[test] +#[should_panic] +fn test_local_beyond_max_datetime() { + let max = FixedOffset::east_opt(2 * 60 * 60).unwrap().from_utc_datetime(&NaiveDateTime::MAX); + let _ = max.naive_local(); +} + #[test] #[cfg(feature = "clock")] fn test_datetime_sub_assign_local() { diff --git a/src/naive/time/mod.rs b/src/naive/time/mod.rs index 121d932985..daeb3fa379 100644 --- a/src/naive/time/mod.rs +++ b/src/naive/time/mod.rs @@ -847,7 +847,7 @@ impl NaiveTime { /// The earliest possible `NaiveTime` pub const MIN: Self = Self { secs: 0, frac: 0 }; - pub(super) const MAX: Self = Self { secs: 23 * 3600 + 59 * 60 + 59, frac: 999_999_999 }; + pub(crate) const MAX: Self = Self { secs: 23 * 3600 + 59 * 60 + 59, frac: 999_999_999 }; } impl Timelike for NaiveTime {