diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
commit | 9835e2ae736235810b4ea1c162ca5e65c547e770 (patch) | |
tree | 3fcebf40ed70e581d776a8a4c65923e8ec20e026 /vendor/chrono/src/datetime/tests.rs | |
parent | Releasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff) | |
download | rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip |
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/chrono/src/datetime/tests.rs')
-rw-r--r-- | vendor/chrono/src/datetime/tests.rs | 952 |
1 files changed, 952 insertions, 0 deletions
diff --git a/vendor/chrono/src/datetime/tests.rs b/vendor/chrono/src/datetime/tests.rs new file mode 100644 index 000000000..ebd32cae9 --- /dev/null +++ b/vendor/chrono/src/datetime/tests.rs @@ -0,0 +1,952 @@ +use std::time::{SystemTime, UNIX_EPOCH}; + +use super::DateTime; +use crate::naive::{NaiveDate, NaiveTime}; +#[cfg(feature = "clock")] +use crate::offset::Local; +use crate::offset::{FixedOffset, TimeZone, Utc}; +use crate::oldtime::Duration; +#[cfg(feature = "clock")] +use crate::Datelike; +use crate::{Days, LocalResult, Months, NaiveDateTime}; + +#[derive(Clone)] +struct DstTester; + +impl DstTester { + fn winter_offset() -> FixedOffset { + FixedOffset::east_opt(8 * 60 * 60).unwrap() + } + fn summer_offset() -> FixedOffset { + FixedOffset::east_opt(9 * 60 * 60).unwrap() + } + + const TO_WINTER_MONTH_DAY: (u32, u32) = (4, 15); + const TO_SUMMER_MONTH_DAY: (u32, u32) = (9, 15); + + fn transition_start_local() -> NaiveTime { + NaiveTime::from_hms_opt(2, 0, 0).unwrap() + } +} + +impl TimeZone for DstTester { + type Offset = FixedOffset; + + fn from_offset(_: &Self::Offset) -> Self { + DstTester + } + + fn offset_from_local_date(&self, _: &NaiveDate) -> crate::LocalResult<Self::Offset> { + unimplemented!() + } + + fn offset_from_local_datetime( + &self, + local: &NaiveDateTime, + ) -> crate::LocalResult<Self::Offset> { + let local_to_winter_transition_start = NaiveDate::from_ymd_opt( + local.year(), + DstTester::TO_WINTER_MONTH_DAY.0, + DstTester::TO_WINTER_MONTH_DAY.1, + ) + .unwrap() + .and_time(DstTester::transition_start_local()); + + let local_to_winter_transition_end = NaiveDate::from_ymd_opt( + local.year(), + DstTester::TO_WINTER_MONTH_DAY.0, + DstTester::TO_WINTER_MONTH_DAY.1, + ) + .unwrap() + .and_time(DstTester::transition_start_local() - Duration::hours(1)); + + let local_to_summer_transition_start = NaiveDate::from_ymd_opt( + local.year(), + DstTester::TO_SUMMER_MONTH_DAY.0, + DstTester::TO_SUMMER_MONTH_DAY.1, + ) + .unwrap() + .and_time(DstTester::transition_start_local()); + + let local_to_summer_transition_end = NaiveDate::from_ymd_opt( + local.year(), + DstTester::TO_SUMMER_MONTH_DAY.0, + DstTester::TO_SUMMER_MONTH_DAY.1, + ) + .unwrap() + .and_time(DstTester::transition_start_local() + Duration::hours(1)); + + if *local < local_to_winter_transition_end || *local >= local_to_summer_transition_end { + LocalResult::Single(DstTester::summer_offset()) + } else if *local >= local_to_winter_transition_start + && *local < local_to_summer_transition_start + { + LocalResult::Single(DstTester::winter_offset()) + } else if *local >= local_to_winter_transition_end + && *local < local_to_winter_transition_start + { + LocalResult::Ambiguous(DstTester::winter_offset(), DstTester::summer_offset()) + } else if *local >= local_to_summer_transition_start + && *local < local_to_summer_transition_end + { + LocalResult::None + } else { + panic!("Unexpected local time {}", local) + } + } + + fn offset_from_utc_date(&self, _: &NaiveDate) -> Self::Offset { + unimplemented!() + } + + fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::Offset { + let utc_to_winter_transition = NaiveDate::from_ymd_opt( + utc.year(), + DstTester::TO_WINTER_MONTH_DAY.0, + DstTester::TO_WINTER_MONTH_DAY.1, + ) + .unwrap() + .and_time(DstTester::transition_start_local()) + - DstTester::summer_offset(); + + let utc_to_summer_transition = NaiveDate::from_ymd_opt( + utc.year(), + DstTester::TO_SUMMER_MONTH_DAY.0, + DstTester::TO_SUMMER_MONTH_DAY.1, + ) + .unwrap() + .and_time(DstTester::transition_start_local()) + - DstTester::winter_offset(); + + if *utc < utc_to_winter_transition || *utc >= utc_to_summer_transition { + DstTester::summer_offset() + } else if *utc >= utc_to_winter_transition && *utc < utc_to_summer_transition { + DstTester::winter_offset() + } else { + panic!("Unexpected utc time {}", utc) + } + } +} + +#[test] +fn test_datetime_add_days() { + let est = FixedOffset::west_opt(5 * 60 * 60).unwrap(); + let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap(); + + assert_eq!( + format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Days::new(5)), + "2014-05-11 07:08:09 -05:00" + ); + assert_eq!( + format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Days::new(5)), + "2014-05-11 07:08:09 +09:00" + ); + + assert_eq!( + format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Days::new(35)), + "2014-06-10 07:08:09 -05:00" + ); + assert_eq!( + format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Days::new(35)), + "2014-06-10 07:08:09 +09:00" + ); + + assert_eq!( + format!("{}", DstTester.with_ymd_and_hms(2014, 4, 6, 7, 8, 9).unwrap() + Days::new(5)), + "2014-04-11 07:08:09 +09:00" + ); + assert_eq!( + format!("{}", DstTester.with_ymd_and_hms(2014, 4, 6, 7, 8, 9).unwrap() + Days::new(10)), + "2014-04-16 07:08:09 +08:00" + ); + + assert_eq!( + format!("{}", DstTester.with_ymd_and_hms(2014, 9, 6, 7, 8, 9).unwrap() + Days::new(5)), + "2014-09-11 07:08:09 +08:00" + ); + assert_eq!( + format!("{}", DstTester.with_ymd_and_hms(2014, 9, 6, 7, 8, 9).unwrap() + Days::new(10)), + "2014-09-16 07:08:09 +09:00" + ); +} + +#[test] +fn test_datetime_sub_days() { + let est = FixedOffset::west_opt(5 * 60 * 60).unwrap(); + let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap(); + + assert_eq!( + format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Days::new(5)), + "2014-05-01 07:08:09 -05:00" + ); + assert_eq!( + format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Days::new(5)), + "2014-05-01 07:08:09 +09:00" + ); + + assert_eq!( + format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Days::new(35)), + "2014-04-01 07:08:09 -05:00" + ); + assert_eq!( + format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Days::new(35)), + "2014-04-01 07:08:09 +09:00" + ); +} + +#[test] +fn test_datetime_add_months() { + let est = FixedOffset::west_opt(5 * 60 * 60).unwrap(); + let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap(); + + assert_eq!( + format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Months::new(1)), + "2014-06-06 07:08:09 -05:00" + ); + assert_eq!( + format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Months::new(1)), + "2014-06-06 07:08:09 +09:00" + ); + + assert_eq!( + format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Months::new(5)), + "2014-10-06 07:08:09 -05:00" + ); + assert_eq!( + format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Months::new(5)), + "2014-10-06 07:08:09 +09:00" + ); +} + +#[test] +fn test_datetime_sub_months() { + let est = FixedOffset::west_opt(5 * 60 * 60).unwrap(); + let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap(); + + assert_eq!( + format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Months::new(1)), + "2014-04-06 07:08:09 -05:00" + ); + assert_eq!( + format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Months::new(1)), + "2014-04-06 07:08:09 +09:00" + ); + + assert_eq!( + format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Months::new(5)), + "2013-12-06 07:08:09 -05:00" + ); + assert_eq!( + format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Months::new(5)), + "2013-12-06 07:08:09 +09:00" + ); +} + +#[test] +fn test_datetime_offset() { + let est = FixedOffset::west_opt(5 * 60 * 60).unwrap(); + let edt = FixedOffset::west_opt(4 * 60 * 60).unwrap(); + let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap(); + + assert_eq!( + format!("{}", Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()), + "2014-05-06 07:08:09 UTC" + ); + assert_eq!( + format!("{}", edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()), + "2014-05-06 07:08:09 -04:00" + ); + assert_eq!( + format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()), + "2014-05-06 07:08:09 +09:00" + ); + assert_eq!( + format!("{:?}", Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()), + "2014-05-06T07:08:09Z" + ); + assert_eq!( + format!("{:?}", edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()), + "2014-05-06T07:08:09-04:00" + ); + assert_eq!( + format!("{:?}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()), + "2014-05-06T07:08:09+09:00" + ); + + // edge cases + assert_eq!( + format!("{:?}", Utc.with_ymd_and_hms(2014, 5, 6, 0, 0, 0).unwrap()), + "2014-05-06T00:00:00Z" + ); + assert_eq!( + format!("{:?}", edt.with_ymd_and_hms(2014, 5, 6, 0, 0, 0).unwrap()), + "2014-05-06T00:00:00-04:00" + ); + assert_eq!( + format!("{:?}", kst.with_ymd_and_hms(2014, 5, 6, 0, 0, 0).unwrap()), + "2014-05-06T00:00:00+09:00" + ); + assert_eq!( + format!("{:?}", Utc.with_ymd_and_hms(2014, 5, 6, 23, 59, 59).unwrap()), + "2014-05-06T23:59:59Z" + ); + assert_eq!( + format!("{:?}", edt.with_ymd_and_hms(2014, 5, 6, 23, 59, 59).unwrap()), + "2014-05-06T23:59:59-04:00" + ); + assert_eq!( + format!("{:?}", kst.with_ymd_and_hms(2014, 5, 6, 23, 59, 59).unwrap()), + "2014-05-06T23:59:59+09:00" + ); + + let dt = Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap(); + assert_eq!(dt, edt.with_ymd_and_hms(2014, 5, 6, 3, 8, 9).unwrap()); + assert_eq!( + dt + Duration::seconds(3600 + 60 + 1), + Utc.with_ymd_and_hms(2014, 5, 6, 8, 9, 10).unwrap() + ); + assert_eq!( + dt.signed_duration_since(edt.with_ymd_and_hms(2014, 5, 6, 10, 11, 12).unwrap()), + Duration::seconds(-7 * 3600 - 3 * 60 - 3) + ); + + assert_eq!(*Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap().offset(), Utc); + assert_eq!(*edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap().offset(), edt); + assert!(*edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap().offset() != est); +} + +#[test] +fn test_datetime_date_and_time() { + let tz = FixedOffset::east_opt(5 * 60 * 60).unwrap(); + let d = tz.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap(); + assert_eq!(d.time(), NaiveTime::from_hms_opt(7, 8, 9).unwrap()); + assert_eq!(d.date_naive(), NaiveDate::from_ymd_opt(2014, 5, 6).unwrap()); + + let tz = FixedOffset::east_opt(4 * 60 * 60).unwrap(); + let d = tz.with_ymd_and_hms(2016, 5, 4, 3, 2, 1).unwrap(); + assert_eq!(d.time(), NaiveTime::from_hms_opt(3, 2, 1).unwrap()); + assert_eq!(d.date_naive(), NaiveDate::from_ymd_opt(2016, 5, 4).unwrap()); + + let tz = FixedOffset::west_opt(13 * 60 * 60).unwrap(); + let d = tz.with_ymd_and_hms(2017, 8, 9, 12, 34, 56).unwrap(); + assert_eq!(d.time(), NaiveTime::from_hms_opt(12, 34, 56).unwrap()); + assert_eq!(d.date_naive(), NaiveDate::from_ymd_opt(2017, 8, 9).unwrap()); + + let utc_d = Utc.with_ymd_and_hms(2017, 8, 9, 12, 34, 56).unwrap(); + assert!(utc_d < d); +} + +#[test] +#[cfg(feature = "clock")] +fn test_datetime_with_timezone() { + let local_now = Local::now(); + let utc_now = local_now.with_timezone(&Utc); + let local_now2 = utc_now.with_timezone(&Local); + assert_eq!(local_now, local_now2); +} + +#[test] +fn test_datetime_rfc2822_and_rfc3339() { + let edt = FixedOffset::east_opt(5 * 60 * 60).unwrap(); + assert_eq!( + Utc.with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap().to_rfc2822(), + "Wed, 18 Feb 2015 23:16:09 +0000" + ); + assert_eq!( + Utc.with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap().to_rfc3339(), + "2015-02-18T23:16:09+00:00" + ); + assert_eq!( + edt.from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap() + ) + .unwrap() + .to_rfc2822(), + "Wed, 18 Feb 2015 23:16:09 +0500" + ); + assert_eq!( + edt.from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap() + ) + .unwrap() + .to_rfc3339(), + "2015-02-18T23:16:09.150+05:00" + ); + assert_eq!( + edt.from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_micro_opt(23, 59, 59, 1_234_567) + .unwrap() + ) + .unwrap() + .to_rfc2822(), + "Wed, 18 Feb 2015 23:59:60 +0500" + ); + assert_eq!( + edt.from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_micro_opt(23, 59, 59, 1_234_567) + .unwrap() + ) + .unwrap() + .to_rfc3339(), + "2015-02-18T23:59:60.234567+05:00" + ); + + assert_eq!( + DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +0000"), + Ok(FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap()) + ); + assert_eq!( + DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 -0000"), + Ok(FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap()) + ); + assert_eq!( + DateTime::parse_from_rfc3339("2015-02-18T23:16:09Z"), + Ok(FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap()) + ); + assert_eq!( + DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:60 +0500"), + Ok(edt + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 59, 59, 1_000) + .unwrap() + ) + .unwrap()) + ); + assert!(DateTime::parse_from_rfc2822("31 DEC 262143 23:59 -2359").is_err()); + assert_eq!( + DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00"), + Ok(edt + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_micro_opt(23, 59, 59, 1_234_567) + .unwrap() + ) + .unwrap()) + ); +} + +#[test] +fn test_rfc3339_opts() { + use crate::SecondsFormat::*; + let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); + let dt = pst + .from_local_datetime( + &NaiveDate::from_ymd_opt(2018, 1, 11) + .unwrap() + .and_hms_nano_opt(10, 5, 13, 84_660_000) + .unwrap(), + ) + .unwrap(); + assert_eq!(dt.to_rfc3339_opts(Secs, false), "2018-01-11T10:05:13+08:00"); + assert_eq!(dt.to_rfc3339_opts(Secs, true), "2018-01-11T10:05:13+08:00"); + assert_eq!(dt.to_rfc3339_opts(Millis, false), "2018-01-11T10:05:13.084+08:00"); + assert_eq!(dt.to_rfc3339_opts(Micros, false), "2018-01-11T10:05:13.084660+08:00"); + assert_eq!(dt.to_rfc3339_opts(Nanos, false), "2018-01-11T10:05:13.084660000+08:00"); + assert_eq!(dt.to_rfc3339_opts(AutoSi, false), "2018-01-11T10:05:13.084660+08:00"); + + let ut = DateTime::<Utc>::from_utc(dt.naive_utc(), Utc); + assert_eq!(ut.to_rfc3339_opts(Secs, false), "2018-01-11T02:05:13+00:00"); + assert_eq!(ut.to_rfc3339_opts(Secs, true), "2018-01-11T02:05:13Z"); + assert_eq!(ut.to_rfc3339_opts(Millis, false), "2018-01-11T02:05:13.084+00:00"); + assert_eq!(ut.to_rfc3339_opts(Millis, true), "2018-01-11T02:05:13.084Z"); + assert_eq!(ut.to_rfc3339_opts(Micros, true), "2018-01-11T02:05:13.084660Z"); + assert_eq!(ut.to_rfc3339_opts(Nanos, true), "2018-01-11T02:05:13.084660000Z"); + assert_eq!(ut.to_rfc3339_opts(AutoSi, true), "2018-01-11T02:05:13.084660Z"); +} + +#[test] +#[should_panic] +fn test_rfc3339_opts_nonexhaustive() { + use crate::SecondsFormat; + let dt = Utc.with_ymd_and_hms(1999, 10, 9, 1, 2, 3).unwrap(); + dt.to_rfc3339_opts(SecondsFormat::__NonExhaustive, true); +} + +#[test] +fn test_datetime_from_str() { + assert_eq!( + "2015-02-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(), + Ok(FixedOffset::east_opt(0) + .unwrap() + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap() + ) + .unwrap()) + ); + assert_eq!( + "2015-02-18T23:16:9.15Z".parse::<DateTime<Utc>>(), + Ok(Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap() + ) + .unwrap()) + ); + assert_eq!( + "2015-02-18T23:16:9.15 UTC".parse::<DateTime<Utc>>(), + Ok(Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap() + ) + .unwrap()) + ); + assert_eq!( + "2015-02-18T23:16:9.15UTC".parse::<DateTime<Utc>>(), + Ok(Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap() + ) + .unwrap()) + ); + + assert_eq!( + "2015-2-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(), + Ok(FixedOffset::east_opt(0) + .unwrap() + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap() + ) + .unwrap()) + ); + assert_eq!( + "2015-2-18T13:16:9.15-10:00".parse::<DateTime<FixedOffset>>(), + Ok(FixedOffset::west_opt(10 * 3600) + .unwrap() + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(13, 16, 9, 150) + .unwrap() + ) + .unwrap()) + ); + assert!("2015-2-18T23:16:9.15".parse::<DateTime<FixedOffset>>().is_err()); + + assert_eq!( + "2015-2-18T23:16:9.15Z".parse::<DateTime<Utc>>(), + Ok(Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap() + ) + .unwrap()) + ); + assert_eq!( + "2015-2-18T13:16:9.15-10:00".parse::<DateTime<Utc>>(), + Ok(Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap() + ) + .unwrap()) + ); + assert!("2015-2-18T23:16:9.15".parse::<DateTime<Utc>>().is_err()); + + // no test for `DateTime<Local>`, we cannot verify that much. +} + +#[test] +fn test_datetime_parse_from_str() { + let ymdhms = |y, m, d, h, n, s, off| { + FixedOffset::east_opt(off).unwrap().with_ymd_and_hms(y, m, d, h, n, s).unwrap() + }; + assert_eq!( + DateTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), + Ok(ymdhms(2014, 5, 7, 12, 34, 56, 570 * 60)) + ); // ignore offset + assert!(DateTime::parse_from_str("20140507000000", "%Y%m%d%H%M%S").is_err()); // no offset + assert!(DateTime::parse_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT") + .is_err()); + assert_eq!( + Utc.datetime_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT"), + Ok(Utc.with_ymd_and_hms(2013, 8, 9, 23, 54, 35).unwrap()) + ); +} + +#[test] +fn test_to_string_round_trip() { + let dt = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap(); + let _dt: DateTime<Utc> = dt.to_string().parse().unwrap(); + + let ndt_fixed = dt.with_timezone(&FixedOffset::east_opt(3600).unwrap()); + let _dt: DateTime<FixedOffset> = ndt_fixed.to_string().parse().unwrap(); + + let ndt_fixed = dt.with_timezone(&FixedOffset::east_opt(0).unwrap()); + let _dt: DateTime<FixedOffset> = ndt_fixed.to_string().parse().unwrap(); +} + +#[test] +#[cfg(feature = "clock")] +fn test_to_string_round_trip_with_local() { + let ndt = Local::now(); + let _dt: DateTime<FixedOffset> = ndt.to_string().parse().unwrap(); +} + +#[test] +#[cfg(feature = "clock")] +fn test_datetime_format_with_local() { + // if we are not around the year boundary, local and UTC date should have the same year + let dt = Local::now().with_month(5).unwrap(); + assert_eq!(dt.format("%Y").to_string(), dt.with_timezone(&Utc).format("%Y").to_string()); +} + +#[test] +#[cfg(feature = "clock")] +fn test_datetime_is_copy() { + // UTC is known to be `Copy`. + let a = Utc::now(); + let b = a; + assert_eq!(a, b); +} + +#[test] +#[cfg(feature = "clock")] +fn test_datetime_is_send() { + use std::thread; + + // UTC is known to be `Send`. + let a = Utc::now(); + thread::spawn(move || { + let _ = a; + }) + .join() + .unwrap(); +} + +#[test] +fn test_subsecond_part() { + let datetime = Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2014, 7, 8) + .unwrap() + .and_hms_nano_opt(9, 10, 11, 1234567) + .unwrap(), + ) + .unwrap(); + + assert_eq!(1, datetime.timestamp_subsec_millis()); + assert_eq!(1234, datetime.timestamp_subsec_micros()); + assert_eq!(1234567, datetime.timestamp_subsec_nanos()); +} + +#[test] +#[cfg(not(target_os = "windows"))] +fn test_from_system_time() { + use std::time::Duration; + + let epoch = Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap(); + let nanos = 999_999_999; + + // SystemTime -> DateTime<Utc> + assert_eq!(DateTime::<Utc>::from(UNIX_EPOCH), epoch); + assert_eq!( + DateTime::<Utc>::from(UNIX_EPOCH + Duration::new(999_999_999, nanos)), + Utc.from_local_datetime( + &NaiveDate::from_ymd_opt(2001, 9, 9) + .unwrap() + .and_hms_nano_opt(1, 46, 39, nanos) + .unwrap() + ) + .unwrap() + ); + assert_eq!( + DateTime::<Utc>::from(UNIX_EPOCH - Duration::new(999_999_999, nanos)), + Utc.from_local_datetime( + &NaiveDate::from_ymd_opt(1938, 4, 24).unwrap().and_hms_nano_opt(22, 13, 20, 1).unwrap() + ) + .unwrap() + ); + + // DateTime<Utc> -> SystemTime + assert_eq!(SystemTime::from(epoch), UNIX_EPOCH); + assert_eq!( + SystemTime::from( + Utc.from_local_datetime( + &NaiveDate::from_ymd_opt(2001, 9, 9) + .unwrap() + .and_hms_nano_opt(1, 46, 39, nanos) + .unwrap() + ) + .unwrap() + ), + UNIX_EPOCH + Duration::new(999_999_999, nanos) + ); + assert_eq!( + SystemTime::from( + Utc.from_local_datetime( + &NaiveDate::from_ymd_opt(1938, 4, 24) + .unwrap() + .and_hms_nano_opt(22, 13, 20, 1) + .unwrap() + ) + .unwrap() + ), + UNIX_EPOCH - Duration::new(999_999_999, 999_999_999) + ); + + // DateTime<any tz> -> SystemTime (via `with_timezone`) + #[cfg(feature = "clock")] + { + assert_eq!(SystemTime::from(epoch.with_timezone(&Local)), UNIX_EPOCH); + } + assert_eq!( + SystemTime::from(epoch.with_timezone(&FixedOffset::east_opt(32400).unwrap())), + UNIX_EPOCH + ); + assert_eq!( + SystemTime::from(epoch.with_timezone(&FixedOffset::west_opt(28800).unwrap())), + UNIX_EPOCH + ); +} + +#[test] +#[cfg(target_os = "windows")] +fn test_from_system_time() { + use std::time::Duration; + + let nanos = 999_999_000; + + let epoch = Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap(); + + // SystemTime -> DateTime<Utc> + assert_eq!(DateTime::<Utc>::from(UNIX_EPOCH), epoch); + assert_eq!( + DateTime::<Utc>::from(UNIX_EPOCH + Duration::new(999_999_999, nanos)), + Utc.from_local_datetime( + &NaiveDate::from_ymd_opt(2001, 9, 9) + .unwrap() + .and_hms_nano_opt(1, 46, 39, nanos) + .unwrap() + ) + .unwrap() + ); + assert_eq!( + DateTime::<Utc>::from(UNIX_EPOCH - Duration::new(999_999_999, nanos)), + Utc.from_local_datetime( + &NaiveDate::from_ymd_opt(1938, 4, 24) + .unwrap() + .and_hms_nano_opt(22, 13, 20, 1_000) + .unwrap() + ) + .unwrap() + ); + + // DateTime<Utc> -> SystemTime + assert_eq!(SystemTime::from(epoch), UNIX_EPOCH); + assert_eq!( + SystemTime::from( + Utc.from_local_datetime( + &NaiveDate::from_ymd_opt(2001, 9, 9) + .unwrap() + .and_hms_nano_opt(1, 46, 39, nanos) + .unwrap() + ) + .unwrap() + ), + UNIX_EPOCH + Duration::new(999_999_999, nanos) + ); + assert_eq!( + SystemTime::from( + Utc.from_local_datetime( + &NaiveDate::from_ymd_opt(1938, 4, 24) + .unwrap() + .and_hms_nano_opt(22, 13, 20, 1_000) + .unwrap() + ) + .unwrap() + ), + UNIX_EPOCH - Duration::new(999_999_999, nanos) + ); + + // DateTime<any tz> -> SystemTime (via `with_timezone`) + #[cfg(feature = "clock")] + { + assert_eq!(SystemTime::from(epoch.with_timezone(&Local)), UNIX_EPOCH); + } + assert_eq!( + SystemTime::from(epoch.with_timezone(&FixedOffset::east_opt(32400).unwrap())), + UNIX_EPOCH + ); + assert_eq!( + SystemTime::from(epoch.with_timezone(&FixedOffset::west_opt(28800).unwrap())), + UNIX_EPOCH + ); +} + +#[test] +fn test_datetime_format_alignment() { + let datetime = Utc.with_ymd_and_hms(2007, 1, 2, 0, 0, 0).unwrap(); + + // Item::Literal + let percent = datetime.format("%%"); + assert_eq!(" %", format!("{:>3}", percent)); + assert_eq!("% ", format!("{:<3}", percent)); + assert_eq!(" % ", format!("{:^3}", percent)); + + // Item::Numeric + let year = datetime.format("%Y"); + assert_eq!(" 2007", format!("{:>6}", year)); + assert_eq!("2007 ", format!("{:<6}", year)); + assert_eq!(" 2007 ", format!("{:^6}", year)); + + // Item::Fixed + let tz = datetime.format("%Z"); + assert_eq!(" UTC", format!("{:>5}", tz)); + assert_eq!("UTC ", format!("{:<5}", tz)); + assert_eq!(" UTC ", format!("{:^5}", tz)); + + // [Item::Numeric, Item::Space, Item::Literal, Item::Space, Item::Numeric] + let ymd = datetime.format("%Y %B %d"); + let ymd_formatted = "2007 January 02"; + assert_eq!(format!(" {}", ymd_formatted), format!("{:>17}", ymd)); + assert_eq!(format!("{} ", ymd_formatted), format!("{:<17}", ymd)); + assert_eq!(format!(" {} ", ymd_formatted), format!("{:^17}", ymd)); +} + +#[test] +fn test_datetime_from_local() { + // 2000-01-12T02:00:00Z + let naivedatetime_utc = + NaiveDate::from_ymd_opt(2000, 1, 12).unwrap().and_hms_opt(2, 0, 0).unwrap(); + let datetime_utc = DateTime::<Utc>::from_utc(naivedatetime_utc, Utc); + + // 2000-01-12T10:00:00+8:00:00 + let timezone_east = FixedOffset::east_opt(8 * 60 * 60).unwrap(); + let naivedatetime_east = + NaiveDate::from_ymd_opt(2000, 1, 12).unwrap().and_hms_opt(10, 0, 0).unwrap(); + let datetime_east = DateTime::<FixedOffset>::from_local(naivedatetime_east, timezone_east); + + // 2000-01-11T19:00:00-7:00:00 + let timezone_west = FixedOffset::west_opt(7 * 60 * 60).unwrap(); + let naivedatetime_west = + NaiveDate::from_ymd_opt(2000, 1, 11).unwrap().and_hms_opt(19, 0, 0).unwrap(); + let datetime_west = DateTime::<FixedOffset>::from_local(naivedatetime_west, timezone_west); + + assert_eq!(datetime_east, datetime_utc.with_timezone(&timezone_east)); + assert_eq!(datetime_west, datetime_utc.with_timezone(&timezone_west)); +} + +#[test] +#[cfg(feature = "clock")] +fn test_years_elapsed() { + const WEEKS_PER_YEAR: f32 = 52.1775; + + // This is always at least one year because 1 year = 52.1775 weeks. + let one_year_ago = + Utc::now().date_naive() - Duration::weeks((WEEKS_PER_YEAR * 1.5).ceil() as i64); + // A bit more than 2 years. + let two_year_ago = + Utc::now().date_naive() - Duration::weeks((WEEKS_PER_YEAR * 2.5).ceil() as i64); + + assert_eq!(Utc::now().date_naive().years_since(one_year_ago), Some(1)); + assert_eq!(Utc::now().date_naive().years_since(two_year_ago), Some(2)); + + // If the given DateTime is later than now, the function will always return 0. + let future = Utc::now().date_naive() + Duration::weeks(12); + assert_eq!(Utc::now().date_naive().years_since(future), None); +} + +#[test] +fn test_datetime_add_assign() { + let naivedatetime = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); + let datetime = DateTime::<Utc>::from_utc(naivedatetime, Utc); + let mut datetime_add = datetime; + + datetime_add += Duration::seconds(60); + assert_eq!(datetime_add, datetime + Duration::seconds(60)); + + let timezone = FixedOffset::east_opt(60 * 60).unwrap(); + let datetime = datetime.with_timezone(&timezone); + let datetime_add = datetime_add.with_timezone(&timezone); + + assert_eq!(datetime_add, datetime + Duration::seconds(60)); + + let timezone = FixedOffset::west_opt(2 * 60 * 60).unwrap(); + let datetime = datetime.with_timezone(&timezone); + let datetime_add = datetime_add.with_timezone(&timezone); + + assert_eq!(datetime_add, datetime + Duration::seconds(60)); +} + +#[test] +#[cfg(feature = "clock")] +fn test_datetime_add_assign_local() { + let naivedatetime = NaiveDate::from_ymd_opt(2022, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); + + let datetime = Local.from_utc_datetime(&naivedatetime); + let mut datetime_add = Local.from_utc_datetime(&naivedatetime); + + // ensure we cross a DST transition + for i in 1..=365 { + datetime_add += Duration::days(1); + assert_eq!(datetime_add, datetime + Duration::days(i)) + } +} + +#[test] +fn test_datetime_sub_assign() { + let naivedatetime = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().and_hms_opt(12, 0, 0).unwrap(); + let datetime = DateTime::<Utc>::from_utc(naivedatetime, Utc); + let mut datetime_sub = datetime; + + datetime_sub -= Duration::minutes(90); + assert_eq!(datetime_sub, datetime - Duration::minutes(90)); + + let timezone = FixedOffset::east_opt(60 * 60).unwrap(); + let datetime = datetime.with_timezone(&timezone); + let datetime_sub = datetime_sub.with_timezone(&timezone); + + assert_eq!(datetime_sub, datetime - Duration::minutes(90)); + + let timezone = FixedOffset::west_opt(2 * 60 * 60).unwrap(); + let datetime = datetime.with_timezone(&timezone); + let datetime_sub = datetime_sub.with_timezone(&timezone); + + assert_eq!(datetime_sub, datetime - Duration::minutes(90)); +} + +#[test] +#[cfg(feature = "clock")] +fn test_datetime_sub_assign_local() { + let naivedatetime = NaiveDate::from_ymd_opt(2022, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); + + let datetime = Local.from_utc_datetime(&naivedatetime); + let mut datetime_sub = Local.from_utc_datetime(&naivedatetime); + + // ensure we cross a DST transition + for i in 1..=365 { + datetime_sub -= Duration::days(1); + assert_eq!(datetime_sub, datetime - Duration::days(i)) + } +} |