diff --git a/src/datetime/tests.rs b/src/datetime/tests.rs index 3872e14b36..3eee30baa1 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; @@ -1331,6 +1331,163 @@ 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 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); + let max_time = NaiveTime::from_hms_nano_opt(23, 59, 59, 999_999_999).unwrap(); + + assert_eq!(format!("{:?}", beyond_min), "-262144-12-31T22:00:00-02:00"); + // RFC 2822 doesn't support years with more than 4 digits. + // assert_eq!(beyond_min.to_rfc2822(), ""); + #[cfg(any(feature = "alloc", feature = "std"))] + assert_eq!(beyond_min.to_rfc3339(), "-262144-12-31T22:00:00-02:00"); + #[cfg(any(feature = "alloc", feature = "std"))] + assert_eq!( + beyond_min.format("%Y-%m-%dT%H:%M:%S%:z").to_string(), + "-262144-12-31T22:00:00-02:00" + ); + assert_eq!(beyond_min.year(), -262144); + assert_eq!(beyond_min.month(), 12); + assert_eq!(beyond_min.month0(), 11); + assert_eq!(beyond_min.day(), 31); + assert_eq!(beyond_min.day0(), 30); + assert_eq!(beyond_min.ordinal(), 366); + assert_eq!(beyond_min.ordinal0(), 365); + assert_eq!(beyond_min.weekday(), Weekday::Wed); + assert_eq!(beyond_min.iso_week().year(), -262143); + assert_eq!(beyond_min.iso_week().week(), 1); + assert_eq!(beyond_min.checked_add_days(Days::new(0)), Some(beyond_min)); + assert_eq!( + beyond_min.checked_add_days(Days::new(1)), + Some(offset_min.from_utc_datetime(&(NaiveDate::MIN + Days(1)).and_time(NaiveTime::MIN))) + ); + assert_eq!(beyond_min.checked_sub_days(Days::new(0)), Some(beyond_min)); + assert_eq!(beyond_min.checked_sub_days(Days::new(1)), None); + assert_eq!(beyond_min.checked_add_months(Months::new(0)), Some(beyond_min)); + assert_eq!( + beyond_min.checked_add_months(Months::new(1)), + Some(offset_min.from_utc_datetime(&(NaiveDate::MIN + Months(1)).and_time(NaiveTime::MIN))) + ); + assert_eq!(beyond_min.checked_sub_months(Months::new(0)), Some(beyond_min)); + assert_eq!(beyond_min.checked_sub_months(Months::new(1)), None); + assert_eq!(beyond_min.with_year(beyond_min.year()), Some(beyond_min)); + let res = NaiveDate::MIN.with_year(2021).unwrap().and_time(NaiveTime::MIN) + offset_min; + assert_eq!(beyond_min.with_year(2020), offset_min.from_local_datetime(&res).single()); + assert_eq!( + offset_min + .from_utc_datetime( + &NaiveDate::from_ymd_opt(2023, 1, 1).unwrap().and_time(NaiveTime::MIN) + ) + .with_year(NaiveDate::MIN.year() - 1), + Some(beyond_min) + ); + 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.hour(), 22); + assert_eq!(beyond_min.minute(), 0); + assert_eq!(beyond_min.second(), 0); + assert_eq!(beyond_min.nanosecond(), 0); + 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!(format!("{:?}", beyond_max), "+262143-01-01T01:59:59.999999999+02:00"); + // RFC 2822 doesn't support years with more than 4 digits. + // assert_eq!(beyond_max.to_rfc2822(), ""); + #[cfg(any(feature = "alloc", feature = "std"))] + assert_eq!(beyond_max.to_rfc3339(), "+262143-01-01T01:59:59.999999999+02:00"); + #[cfg(any(feature = "alloc", feature = "std"))] + assert_eq!( + beyond_max.format("%Y-%m-%dT%H:%M:%S%.9f%:z").to_string(), + "+262143-01-01T01:59:59.999999999+02:00" + ); + assert_eq!(beyond_max.year(), 262143); + assert_eq!(beyond_max.month(), 1); + assert_eq!(beyond_max.month0(), 0); + assert_eq!(beyond_max.day(), 1); + assert_eq!(beyond_max.day0(), 0); + assert_eq!(beyond_max.ordinal(), 1); + assert_eq!(beyond_max.ordinal0(), 0); + assert_eq!(beyond_max.weekday(), Weekday::Tue); + assert_eq!(beyond_max.iso_week().year(), 262143); + assert_eq!(beyond_max.iso_week().week(), 1); + assert_eq!(beyond_max.checked_add_days(Days::new(0)), Some(beyond_max)); + assert_eq!(beyond_max.checked_add_days(Days::new(1)), None); + assert_eq!(beyond_max.checked_sub_days(Days::new(0)), Some(beyond_max)); + assert_eq!( + beyond_max.checked_sub_days(Days::new(1)), + Some(offset_max.from_utc_datetime(&(NaiveDate::MAX - Days(1)).and_time(max_time))) + ); + assert_eq!(beyond_max.checked_add_months(Months::new(0)), Some(beyond_max)); + assert_eq!(beyond_max.checked_add_months(Months::new(1)), None); + assert_eq!(beyond_max.checked_sub_months(Months::new(0)), Some(beyond_max)); + assert_eq!( + beyond_max.checked_sub_months(Months::new(1)), + Some(offset_max.from_utc_datetime(&(NaiveDate::MAX - Months(1)).and_time(max_time))) + ); + assert_eq!(beyond_max.with_year(beyond_max.year()), Some(beyond_max)); + let res = NaiveDate::MAX.with_year(2019).unwrap().and_time(max_time) + offset_max; + assert_eq!(beyond_max.with_year(2020), offset_max.from_local_datetime(&res).single()); + assert_eq!( + offset_max + .from_utc_datetime(&NaiveDate::from_ymd_opt(2023, 12, 31).unwrap().and_time(max_time)) + .with_year(NaiveDate::MAX.year() + 1), + Some(beyond_max) + ); + 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.hour(), 1); + assert_eq!(beyond_max.minute(), 59); + assert_eq!(beyond_max.second(), 59); + assert_eq!(beyond_max.nanosecond(), 999_999_999); + 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() { + 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() {