From 5f8a8e4400d5f6431d0b7de2f2f08584cf42cb0f Mon Sep 17 00:00:00 2001 From: Jacob Pratt Date: Sat, 27 Jan 2024 19:22:01 -0500 Subject: [PATCH] Add `replace_ordinal` methods --- tests/date.rs | 15 ++++++ tests/offset_date_time.rs | 95 +++++++++++++++++++++++---------- tests/primitive_date_time.rs | 15 ++++++ time/src/date.rs | 28 ++++++++++ time/src/offset_date_time.rs | 22 ++++++++ time/src/primitive_date_time.rs | 16 ++++++ 6 files changed, 164 insertions(+), 27 deletions(-) diff --git a/tests/date.rs b/tests/date.rs index 8c77c8e9ec..74ab27db01 100644 --- a/tests/date.rs +++ b/tests/date.rs @@ -1032,6 +1032,21 @@ fn replace_day() { assert!(date!(2022 - 02 - 18).replace_day(30).is_err()); // 30 isn't a valid day in February } +#[test] +fn replace_ordinal() { + assert_eq!( + date!(2022 - 02 - 18).replace_ordinal(1), + Ok(date!(2022 - 001)) + ); + assert_eq!( + date!(2024 - 02 - 29).replace_ordinal(366), + Ok(date!(2024 - 366)) + ); + assert!(date!(2022 - 049).replace_ordinal(0).is_err()); // 0 isn't a valid day + assert!(date!(2022 - 049).replace_ordinal(366).is_err()); // 2022 isn't a leap year + assert!(date!(2022 - 049).replace_ordinal(367).is_err()); // 367 isn't a valid day +} + #[test] fn next_occurrence_test() { assert_eq!( diff --git a/tests/offset_date_time.rs b/tests/offset_date_time.rs index a9593d7d32..ffccf80448 100644 --- a/tests/offset_date_time.rs +++ b/tests/offset_date_time.rs @@ -450,12 +450,16 @@ fn replace_year() -> Result<()> { datetime!(2022 - 02 - 18 12:00 +01).replace_year(2019), Ok(datetime!(2019 - 02 - 18 12:00 +01)) ); - assert!(datetime!(2022 - 02 - 18 12:00 +01) - .replace_year(-1_000_000_000) - .is_err()); // -1_000_000_000 isn't a valid year - assert!(datetime!(2022 - 02 - 18 12:00 +01) - .replace_year(1_000_000_000) - .is_err()); // 1_000_000_000 isn't a valid year + assert!( + datetime!(2022 - 02 - 18 12:00 +01) + .replace_year(-1_000_000_000) + .is_err() + ); // -1_000_000_000 isn't a valid year + assert!( + datetime!(2022 - 02 - 18 12:00 +01) + .replace_year(1_000_000_000) + .is_err() + ); // 1_000_000_000 isn't a valid year Ok(()) } @@ -465,9 +469,11 @@ fn replace_month() -> Result<()> { datetime!(2022 - 02 - 18 12:00 +01).replace_month(Month::January), Ok(datetime!(2022 - 01 - 18 12:00 +01)) ); - assert!(datetime!(2022 - 01 - 30 12:00 +01) - .replace_month(Month::February) - .is_err()); // 30 isn't a valid day in February + assert!( + datetime!(2022 - 01 - 30 12:00 +01) + .replace_month(Month::February) + .is_err() + ); // 30 isn't a valid day in February Ok(()) } @@ -482,15 +488,40 @@ fn replace_day() -> Result<()> { Ok(()) } +#[test] +fn replace_ordinal() { + assert_eq!( + datetime!(2022 - 02 - 18 12:00 +01).replace_ordinal(1), + Ok(datetime!(2022 - 001 12:00 +01)) + ); + assert_eq!( + datetime!(2024 - 02 - 29 12:00 +01).replace_ordinal(366), + Ok(datetime!(2024 - 366 12:00 +01)) + ); + assert!(datetime!(2022 - 049 12:00 +01).replace_ordinal(0).is_err()); // 0 isn't a valid day + assert!( + datetime!(2022 - 049 12:00 +01) + .replace_ordinal(366) + .is_err() + ); // 2022 isn't a leap year + assert!( + datetime!(2022 - 049 12:00 +01) + .replace_ordinal(367) + .is_err() + ); // 367 isn't a valid day +} + #[test] fn replace_hour() -> Result<()> { assert_eq!( datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_hour(7), Ok(datetime!(2022 - 02 - 18 07:02:03.004_005_006 +01)) ); - assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01) - .replace_hour(24) - .is_err()); // 24 isn't a valid hour + assert!( + datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01) + .replace_hour(24) + .is_err() + ); // 24 isn't a valid hour Ok(()) } @@ -500,9 +531,11 @@ fn replace_minute() -> Result<()> { datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_minute(7), Ok(datetime!(2022 - 02 - 18 01:07:03.004_005_006 +01)) ); - assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01) - .replace_minute(60) - .is_err()); // 60 isn't a valid minute + assert!( + datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01) + .replace_minute(60) + .is_err() + ); // 60 isn't a valid minute Ok(()) } @@ -512,9 +545,11 @@ fn replace_second() -> Result<()> { datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_second(7), Ok(datetime!(2022 - 02 - 18 01:02:07.004_005_006 +01)) ); - assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01) - .replace_second(60) - .is_err()); // 60 isn't a valid second + assert!( + datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01) + .replace_second(60) + .is_err() + ); // 60 isn't a valid second Ok(()) } @@ -524,9 +559,11 @@ fn replace_millisecond() -> Result<()> { datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_millisecond(7), Ok(datetime!(2022 - 02 - 18 01:02:03.007 +01)) ); - assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01) - .replace_millisecond(1_000) - .is_err()); // 1_000 isn't a valid millisecond + assert!( + datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01) + .replace_millisecond(1_000) + .is_err() + ); // 1_000 isn't a valid millisecond Ok(()) } @@ -536,9 +573,11 @@ fn replace_microsecond() -> Result<()> { datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_microsecond(7_008), Ok(datetime!(2022 - 02 - 18 01:02:03.007_008 +01)) ); - assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01) - .replace_microsecond(1_000_000) - .is_err()); // 1_000_000 isn't a valid microsecond + assert!( + datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01) + .replace_microsecond(1_000_000) + .is_err() + ); // 1_000_000 isn't a valid microsecond Ok(()) } @@ -548,9 +587,11 @@ fn replace_nanosecond() -> Result<()> { datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_nanosecond(7_008_009), Ok(datetime!(2022 - 02 - 18 01:02:03.007_008_009 +01)) ); - assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01) - .replace_nanosecond(1_000_000_000) - .is_err()); // 1_000_000_000 isn't a valid nanosecond + assert!( + datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01) + .replace_nanosecond(1_000_000_000) + .is_err() + ); // 1_000_000_000 isn't a valid nanosecond Ok(()) } diff --git a/tests/primitive_date_time.rs b/tests/primitive_date_time.rs index 1856640e64..7e7709a5be 100644 --- a/tests/primitive_date_time.rs +++ b/tests/primitive_date_time.rs @@ -291,6 +291,21 @@ fn replace_day() -> Result<()> { Ok(()) } +#[test] +fn replace_ordinal() { + assert_eq!( + datetime!(2022 - 02 - 18 12:00).replace_ordinal(1), + Ok(datetime!(2022 - 001 12:00)) + ); + assert_eq!( + datetime!(2024 - 02 - 29 12:00).replace_ordinal(366), + Ok(datetime!(2024 - 366 12:00)) + ); + assert!(datetime!(2022 - 049 12:00).replace_ordinal(0).is_err()); // 0 isn't a valid day + assert!(datetime!(2022 - 049 12:00).replace_ordinal(366).is_err()); // 2022 isn't a leap year + assert!(datetime!(2022 - 049 12:00).replace_ordinal(367).is_err()); // 367 isn't a valid day +} + #[test] fn replace_hour() -> Result<()> { assert_eq!( diff --git a/time/src/date.rs b/time/src/date.rs index 35b9b1456e..71dfc32a0c 100644 --- a/time/src/date.rs +++ b/time/src/date.rs @@ -1150,6 +1150,34 @@ impl Date { ) }) } + + /// Replace the day of the year. + /// + /// ```rust + /// # use time_macros::date; + /// assert_eq!(date!(2022 - 049).replace_ordinal(1), Ok(date!(2022 - 001))); + /// assert!(date!(2022 - 049).replace_ordinal(0).is_err()); // 0 isn't a valid ordinal + /// assert!(date!(2022 - 049).replace_ordinal(366).is_err()); // 2022 isn't a leap year + /// ```` + #[must_use = "This method does not mutate the original `Date`."] + pub const fn replace_ordinal(self, ordinal: u16) -> Result { + match ordinal { + 1..=365 => {} + 366 if is_leap_year(self.year()) => {} + _ => { + return Err(crate::error::ComponentRange { + name: "ordinal", + minimum: 1, + maximum: days_in_year(self.year()) as _, + value: ordinal as _, + conditional_range: true, + }); + } + } + + // Safety: `ordinal` is in range. + Ok(unsafe { Self::__from_ordinal_date_unchecked(self.year(), ordinal) }) + } // endregion replacement } diff --git a/time/src/offset_date_time.rs b/time/src/offset_date_time.rs index 311305b953..1c495cb383 100644 --- a/time/src/offset_date_time.rs +++ b/time/src/offset_date_time.rs @@ -1079,6 +1079,7 @@ impl OffsetDateTime { /// assert!(datetime!(2022 - 02 - 18 12:00 +01).replace_year(-1_000_000_000).is_err()); // -1_000_000_000 isn't a valid year /// assert!(datetime!(2022 - 02 - 18 12:00 +01).replace_year(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid year /// ``` + #[must_use = "This method does not mutate the original `OffsetDateTime`."] pub const fn replace_year(self, year: i32) -> Result { Ok(const_try!(self.date_time().replace_year(year)).assume_offset(self.offset())) } @@ -1094,6 +1095,7 @@ impl OffsetDateTime { /// ); /// assert!(datetime!(2022 - 01 - 30 12:00 +01).replace_month(Month::February).is_err()); // 30 isn't a valid day in February /// ``` + #[must_use = "This method does not mutate the original `OffsetDateTime`."] pub const fn replace_month(self, month: Month) -> Result { Ok(const_try!(self.date_time().replace_month(month)).assume_offset(self.offset())) } @@ -1109,10 +1111,24 @@ impl OffsetDateTime { /// assert!(datetime!(2022 - 02 - 18 12:00 +01).replace_day(0).is_err()); // 00 isn't a valid day /// assert!(datetime!(2022 - 02 - 18 12:00 +01).replace_day(30).is_err()); // 30 isn't a valid day in February /// ``` + #[must_use = "This method does not mutate the original `OffsetDateTime`."] pub const fn replace_day(self, day: u8) -> Result { Ok(const_try!(self.date_time().replace_day(day)).assume_offset(self.offset())) } + /// Replace the day of the year. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2022-049 12:00 +01).replace_ordinal(1), Ok(datetime!(2022-001 12:00 +01))); + /// assert!(datetime!(2022-049 12:00 +01).replace_ordinal(0).is_err()); // 0 isn't a valid ordinal + /// assert!(datetime!(2022-049 12:00 +01).replace_ordinal(366).is_err()); // 2022 isn't a leap year + /// ``` + #[must_use = "This method does not mutate the original `OffsetDateTime`."] + pub const fn replace_ordinal(self, ordinal: u16) -> Result { + Ok(const_try!(self.date_time().replace_ordinal(ordinal)).assume_offset(self.offset())) + } + /// Replace the clock hour. /// /// ```rust @@ -1123,6 +1139,7 @@ impl OffsetDateTime { /// ); /// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_hour(24).is_err()); // 24 isn't a valid hour /// ``` + #[must_use = "This method does not mutate the original `OffsetDateTime`."] pub const fn replace_hour(self, hour: u8) -> Result { Ok(const_try!(self.date_time().replace_hour(hour)).assume_offset(self.offset())) } @@ -1137,6 +1154,7 @@ impl OffsetDateTime { /// ); /// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_minute(60).is_err()); // 60 isn't a valid minute /// ``` + #[must_use = "This method does not mutate the original `OffsetDateTime`."] pub const fn replace_minute(self, minute: u8) -> Result { Ok(const_try!(self.date_time().replace_minute(minute)).assume_offset(self.offset())) } @@ -1151,6 +1169,7 @@ impl OffsetDateTime { /// ); /// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_second(60).is_err()); // 60 isn't a valid second /// ``` + #[must_use = "This method does not mutate the original `OffsetDateTime`."] pub const fn replace_second(self, second: u8) -> Result { Ok(const_try!(self.date_time().replace_second(second)).assume_offset(self.offset())) } @@ -1165,6 +1184,7 @@ impl OffsetDateTime { /// ); /// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_millisecond(1_000).is_err()); // 1_000 isn't a valid millisecond /// ``` + #[must_use = "This method does not mutate the original `OffsetDateTime`."] pub const fn replace_millisecond( self, millisecond: u16, @@ -1185,6 +1205,7 @@ impl OffsetDateTime { /// ); /// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_microsecond(1_000_000).is_err()); // 1_000_000 isn't a valid microsecond /// ``` + #[must_use = "This method does not mutate the original `OffsetDateTime`."] pub const fn replace_microsecond( self, microsecond: u32, @@ -1205,6 +1226,7 @@ impl OffsetDateTime { /// ); /// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_nanosecond(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond /// ``` + #[must_use = "This method does not mutate the original `OffsetDateTime`."] pub const fn replace_nanosecond(self, nanosecond: u32) -> Result { Ok( const_try!(self.date_time().replace_nanosecond(nanosecond)) diff --git a/time/src/primitive_date_time.rs b/time/src/primitive_date_time.rs index e99e060a1e..74eff7a51e 100644 --- a/time/src/primitive_date_time.rs +++ b/time/src/primitive_date_time.rs @@ -710,6 +710,22 @@ impl PrimitiveDateTime { }) } + /// Replace the day of the year. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2022-049 12:00).replace_ordinal(1), Ok(datetime!(2022-001 12:00))); + /// assert!(datetime!(2022-049 12:00).replace_ordinal(0).is_err()); // 0 isn't a valid ordinal + /// assert!(datetime!(2022-049 12:00).replace_ordinal(366).is_err()); // 2022 isn't a leap year + /// ```` + #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] + pub const fn replace_ordinal(self, ordinal: u16) -> Result { + Ok(Self { + date: const_try!(self.date.replace_ordinal(ordinal)), + time: self.time, + }) + } + /// Replace the clock hour. /// /// ```rust