diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b2f9558..c7c91a41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,39 @@ +0.1.6 (TBD) +=========== +This release includes a new top-level type, `SignedDuration`, that provides a +near exact replica of `std::time::Duration`, but signed. It is meant to provide +alternative APIs for working with durations at a lower level than what `Span` +provides, and to facilitate better integration with the standard library. + +This marks an initial integration phase with `SignedDuration`. It is planned +to integrate it more with the datetime types. Currently, there are integrations +on `Timestamp` and `Span`, but more will be added in the future. + +This release also includes a few deprecations. + +Deprecations: + +* `Timestamp::as_duration`: to be replaced with `SignedDuration` in `jiff 0.2`. +* `Timestamp::from_duration`: to be replaced with `SignedDuration` in +`jiff 0.2`. +* `Timestamp::from_signed_duration`: to be replaced with `SignedDuration` in +`jiff 0.2`. +* `Span::to_duration`: to be replaced with `SignedDuration` in `jiff 0.2`. + +Basically, all of the above APIs either accept or return a +`std::time::Duration`. To avoid breaking chnages at this point, new methods +for `SignedDuration` were added. For example, `Timestamp::as_jiff_duration`. +In `jiff 0.2`, the above deprecated methods will be removed and replaced with +equivalent methods that accept or return a `SignedDuration` instead. Callers +can then convert between a `SignedDuration` and a `std::time::Duration` using +appropriate `TryFrom` trait implementations. + +Enhancements: + +* [#XXX](https://github.com/BurntSushi/jiff/issues/XXX): +Add new top-level `SignedDuration` type. + + 0.1.5 (2024-08-09) ================== This release includes some improvements and bug fixes, particularly for Jiff's diff --git a/src/civil/date.rs b/src/civil/date.rs index ee56cbe7..7efa93cf 100644 --- a/src/civil/date.rs +++ b/src/civil/date.rs @@ -1,6 +1,9 @@ +use core::time::Duration as UnsignedDuration; + use crate::{ civil::{DateTime, Era, ISOWeekDate, Time, Weekday}, - error::{err, Error}, + duration::{Duration, SDuration}, + error::{err, Error, ErrorContext}, fmt::{ self, temporal::{DEFAULT_DATETIME_PARSER, DEFAULT_DATETIME_PRINTER}, @@ -14,7 +17,7 @@ use crate::{ rangeint::{ri16, ri8, RFrom, RInto, TryRFrom}, t::{self, Constant, Day, Month, Sign, UnixEpochDays, Year, C}, }, - RoundMode, Span, SpanRound, Unit, Zoned, + RoundMode, SignedDuration, Span, SpanRound, Unit, Zoned, }; /// A representation of a civil date in the Gregorian calendar. @@ -73,10 +76,10 @@ use crate::{ /// then `d1 < d2`. For example: /// /// ``` -/// use jiff::civil::Date; +/// use jiff::civil::date; /// -/// let d1 = Date::constant(2024, 3, 11); -/// let d2 = Date::constant(2025, 1, 31); +/// let d1 = date(2024, 3, 11); +/// let d2 = date(2025, 1, 31); /// assert!(d1 < d2); /// ``` /// @@ -96,11 +99,11 @@ use crate::{ /// trait implementations. When the result overflows, a panic occurs. /// /// ``` -/// use jiff::{civil::Date, ToSpan}; +/// use jiff::{civil::date, ToSpan}; /// -/// let start = Date::constant(2024, 2, 25); +/// let start = date(2024, 2, 25); /// let one_week_later = start + 1.weeks(); -/// assert_eq!(one_week_later, Date::constant(2024, 3, 3)); +/// assert_eq!(one_week_later, date(2024, 3, 3)); /// ``` /// /// One can compute the span of time between two dates using either @@ -108,10 +111,10 @@ use crate::{ /// `Date` values directly via a `Sub` trait implementation: /// /// ``` -/// use jiff::{civil::Date, ToSpan}; +/// use jiff::{civil::date, ToSpan}; /// -/// let date1 = Date::constant(2024, 3, 3); -/// let date2 = Date::constant(2024, 2, 25); +/// let date1 = date(2024, 3, 3); +/// let date2 = date(2024, 2, 25); /// assert_eq!(date1 - date2, 7.days()); /// ``` /// @@ -120,10 +123,10 @@ use crate::{ /// (as exemplified above), but we can ask for bigger units: /// /// ``` -/// use jiff::{civil::Date, ToSpan, Unit}; +/// use jiff::{civil::date, ToSpan, Unit}; /// -/// let date1 = Date::constant(2024, 5, 3); -/// let date2 = Date::constant(2024, 2, 25); +/// let date1 = date(2024, 5, 3); +/// let date2 = date(2024, 2, 25); /// assert_eq!( /// date1.since((Unit::Year, date2))?, /// 2.months().days(7), @@ -135,10 +138,10 @@ use crate::{ /// Or even round the span returned: /// /// ``` -/// use jiff::{civil::{Date, DateDifference}, RoundMode, ToSpan, Unit}; +/// use jiff::{civil::{DateDifference, date}, RoundMode, ToSpan, Unit}; /// -/// let date1 = Date::constant(2024, 5, 15); -/// let date2 = Date::constant(2024, 2, 25); +/// let date1 = date(2024, 5, 15); +/// let date2 = date(2024, 2, 25); /// assert_eq!( /// date1.since( /// DateDifference::new(date2) @@ -315,27 +318,27 @@ impl Date { /// ISO 8601 week date to a Gregorian date. /// /// ``` - /// use jiff::civil::{Date, ISOWeekDate, Weekday}; + /// use jiff::civil::{Date, ISOWeekDate, Weekday, date}; /// /// let weekdate = ISOWeekDate::new(1994, 52, Weekday::Sunday).unwrap(); - /// let date = Date::from_iso_week_date(weekdate); - /// assert_eq!(date, Date::constant(1995, 1, 1)); + /// let d = Date::from_iso_week_date(weekdate); + /// assert_eq!(d, date(1995, 1, 1)); /// /// let weekdate = ISOWeekDate::new(1997, 1, Weekday::Tuesday).unwrap(); - /// let date = Date::from_iso_week_date(weekdate); - /// assert_eq!(date, Date::constant(1996, 12, 31)); + /// let d = Date::from_iso_week_date(weekdate); + /// assert_eq!(d, date(1996, 12, 31)); /// /// let weekdate = ISOWeekDate::new(2020, 1, Weekday::Monday).unwrap(); - /// let date = Date::from_iso_week_date(weekdate); - /// assert_eq!(date, Date::constant(2019, 12, 30)); + /// let d = Date::from_iso_week_date(weekdate); + /// assert_eq!(d, date(2019, 12, 30)); /// /// let weekdate = ISOWeekDate::new(2024, 10, Weekday::Saturday).unwrap(); - /// let date = Date::from_iso_week_date(weekdate); - /// assert_eq!(date, Date::constant(2024, 3, 9)); + /// let d = Date::from_iso_week_date(weekdate); + /// assert_eq!(d, date(2024, 3, 9)); /// /// let weekdate = ISOWeekDate::new(9999, 52, Weekday::Friday).unwrap(); - /// let date = Date::from_iso_week_date(weekdate); - /// assert_eq!(date, Date::constant(9999, 12, 31)); + /// let d = Date::from_iso_week_date(weekdate); + /// assert_eq!(d, date(9999, 12, 31)); /// ``` #[inline] pub fn from_iso_week_date(weekdate: ISOWeekDate) -> Date { @@ -415,15 +418,15 @@ impl Date { /// # Example /// /// ``` - /// use jiff::civil::Date; + /// use jiff::civil::date; /// - /// let d1 = Date::constant(2024, 3, 9); + /// let d1 = date(2024, 3, 9); /// assert_eq!(d1.year(), 2024); /// - /// let d2 = Date::constant(-2024, 3, 9); + /// let d2 = date(-2024, 3, 9); /// assert_eq!(d2.year(), -2024); /// - /// let d3 = Date::constant(0, 3, 9); + /// let d3 = date(0, 3, 9); /// assert_eq!(d3.year(), 0); /// ``` #[inline] @@ -445,24 +448,24 @@ impl Date { /// # Example /// /// ``` - /// use jiff::civil::{Date, Era}; + /// use jiff::civil::{Era, date}; /// - /// let d = Date::constant(2024, 10, 3); + /// let d = date(2024, 10, 3); /// assert_eq!(d.era_year(), (2024, Era::CE)); /// - /// let d = Date::constant(1, 10, 3); + /// let d = date(1, 10, 3); /// assert_eq!(d.era_year(), (1, Era::CE)); /// - /// let d = Date::constant(0, 10, 3); + /// let d = date(0, 10, 3); /// assert_eq!(d.era_year(), (1, Era::BCE)); /// - /// let d = Date::constant(-1, 10, 3); + /// let d = date(-1, 10, 3); /// assert_eq!(d.era_year(), (2, Era::BCE)); /// - /// let d = Date::constant(-10, 10, 3); + /// let d = date(-10, 10, 3); /// assert_eq!(d.era_year(), (11, Era::BCE)); /// - /// let d = Date::constant(-9_999, 10, 3); + /// let d = date(-9_999, 10, 3); /// assert_eq!(d.era_year(), (10_000, Era::BCE)); /// ``` #[inline] @@ -485,9 +488,9 @@ impl Date { /// # Example /// /// ``` - /// use jiff::civil::Date; + /// use jiff::civil::date; /// - /// let d1 = Date::constant(2024, 3, 9); + /// let d1 = date(2024, 3, 9); /// assert_eq!(d1.month(), 3); /// ``` #[inline] @@ -500,9 +503,9 @@ impl Date { /// # Example /// /// ``` - /// use jiff::civil::Date; + /// use jiff::civil::date; /// - /// let d1 = Date::constant(2024, 2, 29); + /// let d1 = date(2024, 2, 29); /// assert_eq!(d1.day(), 29); /// ``` #[inline] @@ -515,10 +518,10 @@ impl Date { /// # Example /// /// ``` - /// use jiff::civil::{Date, Weekday}; + /// use jiff::civil::{Weekday, date}; /// /// // The Unix epoch was on a Thursday. - /// let d1 = Date::constant(1970, 1, 1); + /// let d1 = date(1970, 1, 1); /// assert_eq!(d1.weekday(), Weekday::Thursday); /// // One can also get the weekday as an offset in a variety of schemes. /// assert_eq!(d1.weekday().to_monday_zero_offset(), 3); @@ -539,15 +542,15 @@ impl Date { /// # Example /// /// ``` - /// use jiff::civil::Date; + /// use jiff::civil::date; /// - /// let d = Date::constant(2006, 8, 24); + /// let d = date(2006, 8, 24); /// assert_eq!(d.day_of_year(), 236); /// - /// let d = Date::constant(2023, 12, 31); + /// let d = date(2023, 12, 31); /// assert_eq!(d.day_of_year(), 365); /// - /// let d = Date::constant(2024, 12, 31); + /// let d = date(2024, 12, 31); /// assert_eq!(d.day_of_year(), 366); /// ``` #[inline] @@ -571,18 +574,18 @@ impl Date { /// # Example /// /// ``` - /// use jiff::civil::Date; + /// use jiff::civil::date; /// - /// let d = Date::constant(2006, 8, 24); + /// let d = date(2006, 8, 24); /// assert_eq!(d.day_of_year_no_leap(), Some(236)); /// - /// let d = Date::constant(2023, 12, 31); + /// let d = date(2023, 12, 31); /// assert_eq!(d.day_of_year_no_leap(), Some(365)); /// - /// let d = Date::constant(2024, 12, 31); + /// let d = date(2024, 12, 31); /// assert_eq!(d.day_of_year_no_leap(), Some(365)); /// - /// let d = Date::constant(2024, 2, 29); + /// let d = date(2024, 2, 29); /// assert_eq!(d.day_of_year_no_leap(), None); /// ``` #[inline] @@ -604,10 +607,10 @@ impl Date { /// # Example /// /// ``` - /// use jiff::civil::Date; + /// use jiff::civil::date; /// - /// let d = Date::constant(2024, 2, 29); - /// assert_eq!(d.first_of_month(), Date::constant(2024, 2, 1)); + /// let d = date(2024, 2, 29); + /// assert_eq!(d.first_of_month(), date(2024, 2, 1)); /// ``` #[inline] pub fn first_of_month(self) -> Date { @@ -620,10 +623,10 @@ impl Date { /// # Example /// /// ``` - /// use jiff::civil::Date; + /// use jiff::civil::date; /// - /// let d = Date::constant(2024, 2, 5); - /// assert_eq!(d.last_of_month(), Date::constant(2024, 2, 29)); + /// let d = date(2024, 2, 5); + /// assert_eq!(d.last_of_month(), date(2024, 2, 29)); /// ``` #[inline] pub fn last_of_month(self) -> Date { @@ -641,15 +644,15 @@ impl Date { /// # Example /// /// ``` - /// use jiff::civil::Date; + /// use jiff::civil::date; /// - /// let d = Date::constant(2024, 2, 10); + /// let d = date(2024, 2, 10); /// assert_eq!(d.days_in_month(), 29); /// - /// let d = Date::constant(2023, 2, 10); + /// let d = date(2023, 2, 10); /// assert_eq!(d.days_in_month(), 28); /// - /// let d = Date::constant(2024, 8, 15); + /// let d = date(2024, 8, 15); /// assert_eq!(d.days_in_month(), 31); /// ``` #[inline] @@ -662,10 +665,10 @@ impl Date { /// # Example /// /// ``` - /// use jiff::civil::Date; + /// use jiff::civil::date; /// - /// let d = Date::constant(2024, 2, 29); - /// assert_eq!(d.first_of_year(), Date::constant(2024, 1, 1)); + /// let d = date(2024, 2, 29); + /// assert_eq!(d.first_of_year(), date(2024, 1, 1)); /// ``` #[inline] pub fn first_of_year(self) -> Date { @@ -678,10 +681,10 @@ impl Date { /// # Example /// /// ``` - /// use jiff::civil::Date; + /// use jiff::civil::date; /// - /// let d = Date::constant(2024, 2, 5); - /// assert_eq!(d.last_of_year(), Date::constant(2024, 12, 31)); + /// let d = date(2024, 2, 5); + /// assert_eq!(d.last_of_year(), date(2024, 12, 31)); /// ``` #[inline] pub fn last_of_year(self) -> Date { @@ -697,12 +700,12 @@ impl Date { /// # Example /// /// ``` - /// use jiff::civil::Date; + /// use jiff::civil::date; /// - /// let d = Date::constant(2024, 7, 10); + /// let d = date(2024, 7, 10); /// assert_eq!(d.days_in_year(), 366); /// - /// let d = Date::constant(2023, 7, 10); + /// let d = date(2023, 7, 10); /// assert_eq!(d.days_in_year(), 365); /// ``` #[inline] @@ -720,10 +723,10 @@ impl Date { /// # Example /// /// ``` - /// use jiff::civil::Date; + /// use jiff::civil::date; /// - /// assert!(Date::constant(2024, 1, 1).in_leap_year()); - /// assert!(!Date::constant(2023, 12, 31).in_leap_year()); + /// assert!(date(2024, 1, 1).in_leap_year()); + /// assert!(!date(2023, 12, 31).in_leap_year()); /// ``` #[inline] pub fn in_leap_year(self) -> bool { @@ -739,10 +742,10 @@ impl Date { /// # Example /// /// ``` - /// use jiff::civil::Date; + /// use jiff::civil::{Date, date}; /// - /// let d = Date::constant(2024, 2, 28); - /// assert_eq!(d.tomorrow()?, Date::constant(2024, 2, 29)); + /// let d = date(2024, 2, 28); + /// assert_eq!(d.tomorrow()?, date(2024, 2, 29)); /// /// // The max doesn't have a tomorrow. /// assert!(Date::MAX.tomorrow().is_err()); @@ -765,10 +768,10 @@ impl Date { /// # Example /// /// ``` - /// use jiff::civil::Date; + /// use jiff::civil::{Date, date}; /// - /// let d = Date::constant(2024, 3, 1); - /// assert_eq!(d.yesterday()?, Date::constant(2024, 2, 29)); + /// let d = date(2024, 3, 1); + /// assert_eq!(d.yesterday()?, date(2024, 2, 29)); /// /// // The min doesn't have a yesterday. /// assert!(Date::MIN.yesterday().is_err()); @@ -801,11 +804,11 @@ impl Date { /// beginning of the month: /// /// ``` - /// use jiff::civil::{Date, Weekday}; + /// use jiff::civil::{Weekday, date}; /// - /// let month = Date::constant(2017, 3, 1); + /// let month = date(2017, 3, 1); /// let second_friday = month.nth_weekday_of_month(2, Weekday::Friday)?; - /// assert_eq!(second_friday, Date::constant(2017, 3, 10)); + /// assert_eq!(second_friday, date(2017, 3, 10)); /// /// # Ok::<(), Box>(()) /// ``` @@ -814,16 +817,16 @@ impl Date { /// weekday in a month: /// /// ``` - /// use jiff::civil::{Date, Weekday}; + /// use jiff::civil::{Weekday, date}; /// - /// let month = Date::constant(2024, 3, 1); + /// let month = date(2024, 3, 1); /// let last_thursday = month.nth_weekday_of_month(-1, Weekday::Thursday)?; - /// assert_eq!(last_thursday, Date::constant(2024, 3, 28)); + /// assert_eq!(last_thursday, date(2024, 3, 28)); /// let second_last_thursday = month.nth_weekday_of_month( /// -2, /// Weekday::Thursday, /// )?; - /// assert_eq!(second_last_thursday, Date::constant(2024, 3, 21)); + /// assert_eq!(second_last_thursday, date(2024, 3, 21)); /// /// # Ok::<(), Box>(()) /// ``` @@ -832,11 +835,11 @@ impl Date { /// for this month. For example, March 2024 only has 4 Mondays: /// /// ``` - /// use jiff::civil::{Date, Weekday}; + /// use jiff::civil::{Weekday, date}; /// - /// let month = Date::constant(2024, 3, 25); + /// let month = date(2024, 3, 25); /// let fourth_monday = month.nth_weekday_of_month(4, Weekday::Monday)?; - /// assert_eq!(fourth_monday, Date::constant(2024, 3, 25)); + /// assert_eq!(fourth_monday, date(2024, 3, 25)); /// // There is no 5th Monday. /// assert!(month.nth_weekday_of_month(5, Weekday::Monday).is_err()); /// // Same goes for counting backwards. @@ -906,24 +909,24 @@ impl Date { /// time: /// /// ``` - /// use jiff::civil::{Date, Weekday}; + /// use jiff::civil::{Weekday, date}; /// /// // Use a Sunday in March as our start date. - /// let d = Date::constant(2024, 3, 10); + /// let d = date(2024, 3, 10); /// assert_eq!(d.weekday(), Weekday::Sunday); /// /// // The first next Monday is tomorrow! /// let next_monday = d.nth_weekday(1, Weekday::Monday)?; - /// assert_eq!(next_monday, Date::constant(2024, 3, 11)); + /// assert_eq!(next_monday, date(2024, 3, 11)); /// /// // But the next Sunday is a week away, because this doesn't /// // include the current weekday. /// let next_sunday = d.nth_weekday(1, Weekday::Sunday)?; - /// assert_eq!(next_sunday, Date::constant(2024, 3, 17)); + /// assert_eq!(next_sunday, date(2024, 3, 17)); /// /// // "not this Thursday, but next Thursday" /// let next_next_thursday = d.nth_weekday(2, Weekday::Thursday)?; - /// assert_eq!(next_next_thursday, Date::constant(2024, 3, 21)); + /// assert_eq!(next_next_thursday, date(2024, 3, 21)); /// /// # Ok::<(), Box>(()) /// ``` @@ -932,23 +935,23 @@ impl Date { /// time: /// /// ``` - /// use jiff::civil::{Date, Weekday}; + /// use jiff::civil::{Weekday, date}; /// /// // Use a Sunday in March as our start date. - /// let d = Date::constant(2024, 3, 10); + /// let d = date(2024, 3, 10); /// assert_eq!(d.weekday(), Weekday::Sunday); /// /// // "last Saturday" was yesterday! /// let last_saturday = d.nth_weekday(-1, Weekday::Saturday)?; - /// assert_eq!(last_saturday, Date::constant(2024, 3, 9)); + /// assert_eq!(last_saturday, date(2024, 3, 9)); /// /// // "last Sunday" was a week ago. /// let last_sunday = d.nth_weekday(-1, Weekday::Sunday)?; - /// assert_eq!(last_sunday, Date::constant(2024, 3, 3)); + /// assert_eq!(last_sunday, date(2024, 3, 3)); /// /// // "not last Thursday, but the one before" /// let prev_prev_thursday = d.nth_weekday(-2, Weekday::Thursday)?; - /// assert_eq!(prev_prev_thursday, Date::constant(2024, 2, 29)); + /// assert_eq!(prev_prev_thursday, date(2024, 2, 29)); /// /// # Ok::<(), Box>(()) /// ``` @@ -975,12 +978,12 @@ impl Date { /// that date using both `nth_weekday` and [`Date::nth_weekday_of_month`]: /// /// ``` - /// use jiff::civil::{Date, Weekday}; + /// use jiff::civil::{Weekday, date}; /// - /// let march = Date::constant(2024, 3, 1); + /// let march = date(2024, 3, 1); /// let last_sunday = march.nth_weekday_of_month(-1, Weekday::Sunday)?; /// let dst_starts_on = last_sunday.nth_weekday(-1, Weekday::Friday)?; - /// assert_eq!(dst_starts_on, Date::constant(2024, 3, 29)); + /// assert_eq!(dst_starts_on, date(2024, 3, 29)); /// /// # Ok::<(), Box>(()) /// ``` @@ -993,15 +996,15 @@ impl Date { /// both. /// /// ``` - /// use jiff::civil::{Date, Weekday}; + /// use jiff::civil::{Weekday, date}; /// - /// let d = Date::constant(2024, 3, 15); + /// let d = date(2024, 3, 15); /// // For weeks starting with Sunday. /// let start_of_week = d.tomorrow()?.nth_weekday(-1, Weekday::Sunday)?; - /// assert_eq!(start_of_week, Date::constant(2024, 3, 10)); + /// assert_eq!(start_of_week, date(2024, 3, 10)); /// // For weeks starting with Monday. /// let start_of_week = d.tomorrow()?.nth_weekday(-1, Weekday::Monday)?; - /// assert_eq!(start_of_week, Date::constant(2024, 3, 11)); + /// assert_eq!(start_of_week, date(2024, 3, 11)); /// /// # Ok::<(), Box>(()) /// ``` @@ -1011,16 +1014,16 @@ impl Date { /// works as expected even at the boundaries of a week: /// /// ``` - /// use jiff::civil::{Date, Weekday}; + /// use jiff::civil::{Weekday, date}; /// /// // The start of the week. - /// let d = Date::constant(2024, 3, 10); + /// let d = date(2024, 3, 10); /// let start_of_week = d.tomorrow()?.nth_weekday(-1, Weekday::Sunday)?; - /// assert_eq!(start_of_week, Date::constant(2024, 3, 10)); + /// assert_eq!(start_of_week, date(2024, 3, 10)); /// // The end of the week. - /// let d = Date::constant(2024, 3, 16); + /// let d = date(2024, 3, 16); /// let start_of_week = d.tomorrow()?.nth_weekday(-1, Weekday::Sunday)?; - /// assert_eq!(start_of_week, Date::constant(2024, 3, 10)); + /// assert_eq!(start_of_week, date(2024, 3, 10)); /// /// # Ok::<(), Box>(()) /// ``` @@ -1072,24 +1075,24 @@ impl Date { /// ISO 8601 week date to a Gregorian date. /// /// ``` - /// use jiff::civil::{Date, Weekday}; + /// use jiff::civil::{Date, Weekday, date}; /// - /// let weekdate = Date::constant(1995, 1, 1).to_iso_week_date(); + /// let weekdate = date(1995, 1, 1).to_iso_week_date(); /// assert_eq!(weekdate.year(), 1994); /// assert_eq!(weekdate.week(), 52); /// assert_eq!(weekdate.weekday(), Weekday::Sunday); /// - /// let weekdate = Date::constant(1996, 12, 31).to_iso_week_date(); + /// let weekdate = date(1996, 12, 31).to_iso_week_date(); /// assert_eq!(weekdate.year(), 1997); /// assert_eq!(weekdate.week(), 1); /// assert_eq!(weekdate.weekday(), Weekday::Tuesday); /// - /// let weekdate = Date::constant(2019, 12, 30).to_iso_week_date(); + /// let weekdate = date(2019, 12, 30).to_iso_week_date(); /// assert_eq!(weekdate.year(), 2020); /// assert_eq!(weekdate.week(), 1); /// assert_eq!(weekdate.weekday(), Weekday::Monday); /// - /// let weekdate = Date::constant(2024, 3, 9).to_iso_week_date(); + /// let weekdate = date(2024, 3, 9).to_iso_week_date(); /// assert_eq!(weekdate.year(), 2024); /// assert_eq!(weekdate.week(), 10); /// assert_eq!(weekdate.weekday(), Weekday::Saturday); @@ -1260,10 +1263,10 @@ impl Date { /// # Example /// /// ``` - /// use jiff::civil::{Date, DateTime, Time}; + /// use jiff::civil::{DateTime, date, time}; /// - /// let date = Date::constant(2010, 3, 14); - /// let time = Time::constant(2, 30, 0, 0); + /// let date = date(2010, 3, 14); + /// let time = time(2, 30, 0, 0); /// assert_eq!(DateTime::from_parts(date, time), date.to_datetime(time)); /// ``` #[inline] @@ -1312,16 +1315,21 @@ impl Date { /// Add the given span of time to this date. If the sum would overflow the /// minimum or maximum date values, then an error is returned. /// + /// This operation accepts three different duration types: [`Span`], + /// [`SignedDuration`] or [`std::time::Duration`]. This is achieved via + /// `From` trait implementations for the [`DateArithmetic`] type. + /// /// # Properties /// - /// This routine is _not_ commutative because some additions may be - /// ambiguous. For example, adding `1 month` to the date `2024-03-31` will - /// produce `2024-04-30` since April has only 30 days in a month. Moreover, - /// subtracting `1 month` from `2024-04-30` will produce `2024-03-30`, - /// which is not the date we started with. + /// When adding a [`Span`] duration, this routine is _not_ reversible + /// because some additions may be ambiguous. For example, adding `1 month` + /// to the date `2024-03-31` will produce `2024-04-30` since April has only + /// 30 days in a month. Conversely, subtracting `1 month` from `2024-04-30` + /// will produce `2024-03-30`, which is not the date we started with. /// /// If spans of time are limited to units of days (or less), then this - /// routine _is_ commutative. + /// routine _is_ reversible. This also implies that all operations with + /// a [`SignedDuration`] or a [`std::time::Duration`] are reversible. /// /// # Errors /// @@ -1335,19 +1343,19 @@ impl Date { /// creation of spans. /// /// ``` - /// use jiff::{civil::Date, ToSpan}; + /// use jiff::{civil::date, ToSpan}; /// - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!(d.checked_add(1.months())?, Date::constant(2024, 4, 30)); + /// let d = date(2024, 3, 31); + /// assert_eq!(d.checked_add(1.months())?, date(2024, 4, 30)); /// // Adding two months gives us May 31, not May 30. - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!(d.checked_add(2.months())?, Date::constant(2024, 5, 31)); + /// let d = date(2024, 3, 31); + /// assert_eq!(d.checked_add(2.months())?, date(2024, 5, 31)); /// // Any time in the span that does not exceed a day is ignored. - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!(d.checked_add(23.hours())?, Date::constant(2024, 3, 31)); + /// let d = date(2024, 3, 31); + /// assert_eq!(d.checked_add(23.hours())?, date(2024, 3, 31)); /// // But if the time exceeds a day, that is accounted for! - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!(d.checked_add(28.hours())?, Date::constant(2024, 4, 1)); + /// let d = date(2024, 3, 31); + /// assert_eq!(d.checked_add(28.hours())?, date(2024, 4, 1)); /// /// # Ok::<(), Box>(()) /// ``` @@ -1358,21 +1366,21 @@ impl Date { /// fails, it will result in a panic. /// /// ``` - /// use jiff::{civil::Date, ToSpan}; + /// use jiff::{civil::date, ToSpan}; /// - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!(d + 1.months(), Date::constant(2024, 4, 30)); + /// let d = date(2024, 3, 31); + /// assert_eq!(d + 1.months(), date(2024, 4, 30)); /// ``` /// /// # Example: negative spans are supported /// /// ``` - /// use jiff::{civil::Date, ToSpan}; + /// use jiff::{civil::date, ToSpan}; /// - /// let d = Date::constant(2024, 3, 31); + /// let d = date(2024, 3, 31); /// assert_eq!( /// d.checked_add(-1.months())?, - /// Date::constant(2024, 2, 29), + /// date(2024, 2, 29), /// ); /// # Ok::<(), Box>(()) /// ``` @@ -1380,14 +1388,48 @@ impl Date { /// # Example: error on overflow /// /// ``` - /// use jiff::{civil::Date, ToSpan}; + /// use jiff::{civil::date, ToSpan}; /// - /// let d = Date::constant(2024, 3, 31); + /// let d = date(2024, 3, 31); /// assert!(d.checked_add(9000.years()).is_err()); /// assert!(d.checked_add(-19000.years()).is_err()); /// ``` + /// + /// # Example: adding absolute durations + /// + /// This shows how to add signed and unsigned absolute durations to a + /// `Date`. Only whole numbers of days are considered. Since this is a + /// civil date unaware of time zones, days are always 24 hours. + /// + /// ``` + /// use std::time::Duration; + /// + /// use jiff::{civil::date, SignedDuration}; + /// + /// let d = date(2024, 2, 29); + /// + /// let dur = SignedDuration::from_hours(24); + /// assert_eq!(d.checked_add(dur)?, date(2024, 3, 1)); + /// assert_eq!(d.checked_add(-dur)?, date(2024, 2, 28)); + /// + /// // Any leftover time is truncated. That is, only + /// // whole days from the duration are considered. + /// let dur = Duration::from_secs((24 * 60 * 60) + (23 * 60 * 60)); + /// assert_eq!(d.checked_add(dur)?, date(2024, 3, 1)); + /// + /// # Ok::<(), Box>(()) + /// ``` #[inline] - pub fn checked_add(self, span: Span) -> Result { + pub fn checked_add>( + self, + duration: A, + ) -> Result { + let duration: DateArithmetic = duration.into(); + duration.checked_add(self) + } + + #[inline] + fn checked_add_span(self, span: Span) -> Result { let (month, years) = month_add_overflowing(self.month, span.get_months_ranged()); let year = self @@ -1413,150 +1455,79 @@ impl Date { Ok(Date::from_unix_epoch_days(days)) } - /// Subtract the given span of time from this date. If the difference would - /// overflow the minimum or maximum date values, then an error is returned. - /// - /// # Properties - /// - /// This routine is _not_ commutative because some additions may be - /// ambiguous. For example, adding `1 month` to the date `2024-03-31` will - /// produce `2024-04-30` since April has only 30 days in a month. Moreover, - /// subtracting `1 month` from `2024-04-30` will produce `2024-03-30`, - /// which is not the date we started with. - /// - /// If spans of time are limited to units of days (or less), then this - /// routine _is_ commutative. + #[inline] + fn checked_add_duration( + self, + duration: SignedDuration, + ) -> Result { + // OK because 24!={-1,0}. + let days = duration.as_hours() / 24; + let days = + UnixEpochDays::try_new("days", days).with_context(|| { + err!( + "{days} computed from duration {duration:?} overflows \ + Jiff's datetime limits", + ) + })?; + let days = self.to_unix_epoch_days().try_checked_add("days", days)?; + Ok(Date::from_unix_epoch_days(days)) + } + + /// This routine is identical to [`Date::checked_add`] with the duration + /// negated. /// /// # Errors /// - /// If the span subtracted from this date would result in a date that - /// exceeds the range of a `Date`, then this will return an error. - /// - /// # Examples - /// - /// This shows a few examples of subtracting spans of time to various - /// dates. We make use of the [`ToSpan`](crate::ToSpan) trait for - /// convenient creation of spans. - /// - /// ``` - /// use jiff::{civil::Date, ToSpan}; + /// This has the same error conditions as [`Date::checked_add`]. /// - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!(d.checked_sub(1.months())?, Date::constant(2024, 2, 29)); - /// // Adding subtracting two months gives us Jan 31, not Jan 30. - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!(d.checked_sub(2.months())?, Date::constant(2024, 1, 31)); - /// // Any time in the span that does not exceed a day is ignored. - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!(d.checked_sub(23.hours())?, Date::constant(2024, 3, 31)); - /// // But if the time exceeds a day, that is accounted for! - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!(d.checked_sub(28.hours())?, Date::constant(2024, 3, 30)); + /// # Example /// - /// # Ok::<(), Box>(()) /// ``` + /// use std::time::Duration; /// - /// # Example: available via subtraction operator - /// - /// This routine can be used via the `-` operator. Note though that if it - /// fails, it will result in a panic. - /// - /// ``` - /// use jiff::{civil::Date, ToSpan}; + /// use jiff::{civil::date, SignedDuration, ToSpan}; /// - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!(d - 1.months(), Date::constant(2024, 2, 29)); - /// ``` + /// let d = date(2024, 2, 29); + /// assert_eq!(d.checked_sub(1.year())?, date(2023, 2, 28)); /// - /// # Example: negative spans are supported + /// let dur = SignedDuration::from_hours(24); + /// assert_eq!(d.checked_sub(dur)?, date(2024, 2, 28)); /// - /// ``` - /// use jiff::{civil::Date, ToSpan}; + /// let dur = Duration::from_secs(24 * 60 * 60); + /// assert_eq!(d.checked_sub(dur)?, date(2024, 2, 28)); /// - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!( - /// d.checked_sub(-1.months())?, - /// Date::constant(2024, 4, 30), - /// ); /// # Ok::<(), Box>(()) /// ``` - /// - /// # Example: error on overflow - /// - /// ``` - /// use jiff::{civil::Date, ToSpan}; - /// - /// let d = Date::constant(2024, 3, 31); - /// assert!(d.checked_sub(19000.years()).is_err()); - /// assert!(d.checked_sub(-9000.years()).is_err()); - /// ``` #[inline] - pub fn checked_sub(self, span: Span) -> Result { - self.checked_add(span.negate()) + pub fn checked_sub>( + self, + duration: A, + ) -> Result { + let duration: DateArithmetic = duration.into(); + duration.checked_neg().and_then(|da| da.checked_add(self)) } - /// Add the given span of time to this date. If the sum would overflow the - /// minimum or maximum date values, then the result saturates. - /// - /// # Properties - /// - /// This routine is _not_ commutative (even putting aside cases where - /// saturation occurs) because some additions may be ambiguous. For - /// example, adding `1 month` to the date `2024-03-31` will produce - /// `2024-04-30` since April has only 30 days in a month. Moreover, - /// subtracting `1 month` from `2024-04-30` will produce `2024-03-30`, - /// which is not the date we started with. + /// This routine is identical to [`Date::checked_add`], except the + /// result saturates on overflow. That is, instead of overflow, either + /// [`Date::MIN`] or [`Date::MAX`] is returned. /// - /// If spans of time are limited to units of days (or less), then this - /// routine _is_ commutative. - /// - /// # Examples - /// - /// This shows a few examples of adding spans of time to various dates. - /// We make use of the [`ToSpan`](crate::ToSpan) trait for convenient - /// creation of spans. - /// - /// ``` - /// use jiff::{civil::Date, ToSpan}; - /// - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!(d.saturating_add(1.months()), Date::constant(2024, 4, 30)); - /// // Adding two months gives us May 31, not May 30. - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!(d.saturating_add(2.months()), Date::constant(2024, 5, 31)); - /// // Any time in the span that does not exceed a day is ignored. - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!(d.saturating_add(23.hours()), Date::constant(2024, 3, 31)); - /// // But if the time exceeds a day, that is accounted for! - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!(d.saturating_add(28.hours()), Date::constant(2024, 4, 1)); - /// ``` - /// - /// # Example: negative spans are supported - /// - /// ``` - /// use jiff::{civil::Date, ToSpan}; - /// - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!( - /// d.saturating_add(-1.months()), - /// Date::constant(2024, 2, 29), - /// ); - /// ``` - /// - /// # Example: saturation on overflow + /// # Example /// /// ``` - /// use jiff::{civil::Date, ToSpan}; + /// use jiff::{civil::{Date, date}, SignedDuration, ToSpan}; /// - /// let d = Date::constant(2024, 3, 31); + /// let d = date(2024, 3, 31); /// assert_eq!(Date::MAX, d.saturating_add(9000.years())); /// assert_eq!(Date::MIN, d.saturating_add(-19000.years())); + /// assert_eq!(Date::MAX, d.saturating_add(SignedDuration::MAX)); + /// assert_eq!(Date::MIN, d.saturating_add(SignedDuration::MIN)); + /// assert_eq!(Date::MAX, d.saturating_add(std::time::Duration::MAX)); /// ``` #[inline] - pub fn saturating_add(self, span: Span) -> Date { - self.checked_add(span).unwrap_or_else(|_| { - if span.is_negative() { + pub fn saturating_add>(self, duration: A) -> Date { + let duration: DateArithmetic = duration.into(); + self.checked_add(duration).unwrap_or_else(|_| { + if duration.is_negative() { Date::MIN } else { Date::MAX @@ -1564,73 +1535,32 @@ impl Date { }) } - /// Subtract the given span of time from this date. If the difference would - /// overflow the minimum or maximum date values, then the result saturates. - /// - /// # Properties - /// - /// This routine is _not_ commutative (even putting aside cases where - /// saturation occurs) because some additions may be ambiguous. For - /// example, adding `1 month` to the date `2024-03-31` will produce - /// `2024-04-30` since April has only 30 days in a month. Moreover, - /// subtracting `1 month` from `2024-04-30` will produce `2024-03-30`, - /// which is not the date we started with. + /// This routine is identical to [`Date::saturating_add`] with the span + /// parameter negated. /// - /// If spans of time are limited to units of days (or less), then this - /// routine _is_ commutative. - /// - /// # Examples - /// - /// This shows a few examples of subtracting spans of time to various - /// dates. We make use of the [`ToSpan`](crate::ToSpan) trait for - /// convenient creation of spans. - /// - /// ``` - /// use jiff::{civil::Date, ToSpan}; - /// - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!(d.saturating_sub(1.months()), Date::constant(2024, 2, 29)); - /// // Adding subtracting two months gives us Jan 31, not Jan 30. - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!(d.saturating_sub(2.months()), Date::constant(2024, 1, 31)); - /// // Any time in the span that does not exceed a day is ignored. - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!(d.saturating_sub(23.hours()), Date::constant(2024, 3, 31)); - /// // But if the time exceeds a day, that is accounted for! - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!(d.saturating_sub(28.hours()), Date::constant(2024, 3, 30)); - /// ``` - /// - /// # Example: negative spans are supported - /// - /// ``` - /// use jiff::{civil::Date, ToSpan}; - /// - /// let d = Date::constant(2024, 3, 31); - /// assert_eq!( - /// d.saturating_sub(-1.months()), - /// Date::constant(2024, 4, 30), - /// ); - /// ``` - /// - /// # Example: saturation on overflow + /// # Example /// /// ``` - /// use jiff::{civil::Date, ToSpan}; + /// use jiff::{civil::{Date, date}, SignedDuration, ToSpan}; /// - /// let d = Date::constant(2024, 3, 31); + /// let d = date(2024, 3, 31); /// assert_eq!(Date::MIN, d.saturating_sub(19000.years())); /// assert_eq!(Date::MAX, d.saturating_sub(-9000.years())); + /// assert_eq!(Date::MIN, d.saturating_sub(SignedDuration::MAX)); + /// assert_eq!(Date::MAX, d.saturating_sub(SignedDuration::MIN)); + /// assert_eq!(Date::MIN, d.saturating_sub(std::time::Duration::MAX)); /// ``` #[inline] - pub fn saturating_sub(self, span: Span) -> Date { - self.saturating_add(span.negate()) + pub fn saturating_sub>(self, duration: A) -> Date { + let duration: DateArithmetic = duration.into(); + let Ok(duration) = duration.checked_neg() else { return Date::MIN }; + self.saturating_add(duration) } - /// Returns a span representing the elapsed time from this date since + /// Returns a span representing the elapsed time from this date until /// the given `other` date. /// - /// When `other` occurs after this date, then the span returned will be + /// When `other` occurs before this date, then the span returned will be /// negative. /// /// Depending on the input provided, the span returned is rounded. It may @@ -1646,14 +1576,14 @@ impl Date { /// /// # Properties /// - /// It is guaranteed that if the returned span is added to `other`, and if - /// no rounding is requested, and if the largest unit requested is at most - /// `Unit::Day`, then the original date will be returned. + /// It is guaranteed that if the returned span is subtracted from `other`, + /// and if no rounding is requested, and if the largest unit request is at + /// most `Unit::Day`, then the original date will be returned. /// - /// This routine is equivalent to `self.until(other).map(|span| -span)` + /// This routine is equivalent to `self.since(other).map(|span| -span)` /// if no rounding options are set. If rounding options are set, then /// it's equivalent to - /// `self.until(other_without_rounding_options).map(|span| -span)`, + /// `self.since(other_without_rounding_options).map(|span| -span)`, /// followed by a call to [`Span::round`] with the appropriate rounding /// options set. This is because the negation of a span can result in /// different rounding results depending on the rounding mode. @@ -1669,34 +1599,20 @@ impl Date { /// # Examples /// /// ``` - /// use jiff::{civil::Date, ToSpan}; + /// use jiff::{civil::date, ToSpan}; /// - /// let earlier = Date::constant(2006, 8, 24); - /// let later = Date::constant(2019, 1, 31); - /// assert_eq!(later.since(earlier)?, 4543.days()); + /// let earlier = date(2006, 8, 24); + /// let later = date(2019, 1, 31); + /// assert_eq!(earlier.until(later)?, 4543.days()); /// /// // Flipping the dates is fine, but you'll get a negative span. - /// let earlier = Date::constant(2006, 8, 24); - /// let later = Date::constant(2019, 1, 31); - /// assert_eq!(earlier.since(later)?, -4543.days()); + /// let earlier = date(2006, 8, 24); + /// let later = date(2019, 1, 31); + /// assert_eq!(later.until(earlier)?, -4543.days()); /// /// # Ok::<(), Box>(()) /// ``` /// - /// # Example: available via subtraction operator - /// - /// This routine can be used via the `-` operator. Since the default - /// configuration is used and because a `Span` can represent the difference - /// between any two possible dates, it will never panic. - /// - /// ``` - /// use jiff::{civil::Date, ToSpan}; - /// - /// let earlier = Date::constant(2006, 8, 24); - /// let later = Date::constant(2019, 1, 31); - /// assert_eq!(later - earlier, 4543.days()); - /// ``` - /// /// # Example: using bigger units /// /// This example shows how to expand the span returned to bigger units. @@ -1704,17 +1620,17 @@ impl Date { /// implementation. /// /// ``` - /// use jiff::{civil::Date, Unit, ToSpan}; + /// use jiff::{civil::date, Unit, ToSpan}; /// - /// let d1 = Date::constant(1995, 12, 07); - /// let d2 = Date::constant(2019, 01, 31); + /// let d1 = date(1995, 12, 07); + /// let d2 = date(2019, 01, 31); /// /// // The default limits durations to using "days" as the biggest unit. - /// let span = d2.since(d1)?; + /// let span = d1.until(d2)?; /// assert_eq!(span.to_string(), "P8456d"); /// /// // But we can ask for units all the way up to years. - /// let span = d2.since((Unit::Year, d1))?; + /// let span = d1.until((Unit::Year, d2))?; /// assert_eq!(span.to_string(), "P23y1m24d"); /// /// # Ok::<(), Box>(()) @@ -1729,17 +1645,17 @@ impl Date { /// in order to gain full configurability. /// /// ``` - /// use jiff::{civil::{Date, DateDifference}, Unit, ToSpan}; + /// use jiff::{civil::{date, DateDifference}, Unit, ToSpan}; /// - /// let d1 = Date::constant(1995, 12, 07); - /// let d2 = Date::constant(2019, 02, 06); + /// let d1 = date(1995, 12, 07); + /// let d2 = date(2019, 02, 06); /// - /// let span = d2.since(DateDifference::from(d1).smallest(Unit::Month))?; + /// let span = d1.until(DateDifference::from(d2).smallest(Unit::Month))?; /// assert_eq!(span, 277.months()); /// /// // Or even include years to make the span a bit more comprehensible. - /// let span = d2.since( - /// DateDifference::from(d1) + /// let span = d1.until( + /// DateDifference::from(d2) /// .smallest(Unit::Month) /// .largest(Unit::Year), /// )?; @@ -1757,23 +1673,23 @@ impl Date { /// original date. For example: /// /// ``` - /// use jiff::{civil::Date, Unit, ToSpan}; + /// use jiff::{civil::date, Unit, ToSpan}; /// - /// let d1 = Date::constant(2024, 3, 2); - /// let d2 = Date::constant(2024, 5, 1); + /// let d1 = date(2024, 3, 2); + /// let d2 = date(2024, 5, 1); /// - /// let span = d2.since((Unit::Month, d1))?; - /// assert_eq!(span, 1.month().days(30)); - /// let maybe_original = d1.checked_add(span)?; + /// let span = d1.until((Unit::Month, d2))?; + /// assert_eq!(span, 1.month().days(29)); + /// let maybe_original = d2.checked_sub(span)?; /// // Not the same as the original datetime! - /// assert_eq!(maybe_original, Date::constant(2024, 5, 2)); + /// assert_eq!(maybe_original, date(2024, 3, 3)); /// /// // But in the default configuration, days are always the biggest unit /// // and reversibility is guaranteed. - /// let span = d2.since(d1)?; + /// let span = d1.until(d2)?; /// assert_eq!(span, 60.days()); - /// let is_original = d1.checked_add(span)?; - /// assert_eq!(is_original, d2); + /// let is_original = d2.checked_sub(span)?; + /// assert_eq!(is_original, d1); /// /// # Ok::<(), Box>(()) /// ``` @@ -1784,12 +1700,12 @@ impl Date { /// case, adding one month to `2024-03-02` corresponds to 31 days, but /// subtracting one month from `2024-05-01` corresponds to 30 days. #[inline] - pub fn since>( + pub fn until>( self, other: A, ) -> Result { let args: DateDifference = other.into(); - let span = -args.until_with_largest_unit(self)?; + let span = args.until_with_largest_unit(self)?; if args.rounding_may_change_span() { span.round(args.round.relative(self)) } else { @@ -1797,160 +1713,161 @@ impl Date { } } - /// Returns a span representing the elapsed time from this date until - /// the given `other` date. + /// This routine is identical to [`Date::until`], but the order of the + /// parameters is flipped. /// - /// When `other` occurs before this date, then the span returned will be - /// negative. + /// # Errors /// - /// Depending on the input provided, the span returned is rounded. It may - /// also be balanced up to bigger units than the default. By default, the - /// span returned is balanced such that the biggest and smallest possible - /// unit is days. + /// This has the same error conditions as [`Date::until`]. /// - /// This operation is configured by providing a [`DateDifference`] - /// value. Since this routine accepts anything that implements - /// `Into`, once can pass a `Date` directly. One - /// can also pass a `(Unit, Date)`, where `Unit` is treated as - /// [`DateDifference::largest`]. + /// # Example /// - /// # Properties + /// This routine can be used via the `-` operator. Since the default + /// configuration is used and because a `Span` can represent the difference + /// between any two possible dates, it will never panic. /// - /// It is guaranteed that if the returned span is subtracted from `other`, - /// and if no rounding is requested, and if the largest unit request is at - /// most `Unit::Day`, then the original date will be returned. + /// ``` + /// use jiff::{civil::date, ToSpan}; /// - /// This routine is equivalent to `self.since(other).map(|span| -span)` - /// if no rounding options are set. If rounding options are set, then - /// it's equivalent to - /// `self.since(other_without_rounding_options).map(|span| -span)`, - /// followed by a call to [`Span::round`] with the appropriate rounding - /// options set. This is because the negation of a span can result in - /// different rounding results depending on the rounding mode. + /// let earlier = date(2006, 8, 24); + /// let later = date(2019, 1, 31); + /// assert_eq!(later - earlier, 4543.days()); + /// // Equivalent to: + /// assert_eq!(later.since(earlier).unwrap(), 4543.days()); + /// ``` + #[inline] + pub fn since>( + self, + other: A, + ) -> Result { + let args: DateDifference = other.into(); + let span = -args.until_with_largest_unit(self)?; + if args.rounding_may_change_span() { + span.round(args.round.relative(self)) + } else { + Ok(span) + } + } + + /// Returns an absolute duration representing the elapsed time from this + /// date until the given `other` date. /// - /// # Errors + /// When `other` occurs before this date, then the duration returned will + /// be negative. /// - /// An error can occur if `DateDifference` is misconfigured. For example, - /// if the smallest unit provided is bigger than the largest unit. + /// Unlike [`Date::until`], this returns a duration corresponding to a + /// 96-bit integer of nanoseconds between two dates. In this case of + /// computing durations between civil dates where all days are assumed to + /// be 24 hours long, the duration returned will always be divisible by + /// 24 hours. (That is, `24 * 60 * 60 * 1_000_000_000` nanoseconds.) /// - /// It is guaranteed that if one provides a date with the default - /// [`DateDifference`] configuration, then this routine will never fail. + /// # Fallibility /// - /// # Examples + /// This routine never panics or returns an error. Since there are no + /// configuration options that can be incorrectly provided, no error is + /// possible when calling this routine. In contrast, [`Date::until`] can + /// return an error in some cases due to misconfiguration. But like this + /// routine, [`Date::until`] never panics or returns an error in its + /// default configuration. /// - /// ``` - /// use jiff::{civil::Date, ToSpan}; + /// # When should I use this versus [`Date::until`]? /// - /// let earlier = Date::constant(2006, 8, 24); - /// let later = Date::constant(2019, 1, 31); - /// assert_eq!(earlier.until(later)?, 4543.days()); + /// See the type documentation for [`SignedDuration`] for the section on + /// when one should use [`Span`] and when one should use `SignedDuration`. + /// In short, use `Span` (and therefore `Date::until`) unless you have a + /// specific reason to do otherwise. /// - /// // Flipping the dates is fine, but you'll get a negative span. - /// let earlier = Date::constant(2006, 8, 24); - /// let later = Date::constant(2019, 1, 31); - /// assert_eq!(later.until(earlier)?, -4543.days()); + /// # Example /// - /// # Ok::<(), Box>(()) /// ``` + /// use jiff::{civil::date, SignedDuration}; /// - /// # Example: using bigger units + /// let earlier = date(2006, 8, 24); + /// let later = date(2019, 1, 31); + /// assert_eq!( + /// earlier.duration_until(later), + /// SignedDuration::from_hours(4543 * 24), + /// ); + /// ``` /// - /// This example shows how to expand the span returned to bigger units. - /// This makes use of a `From<(Unit, Date)> for DateDifference` trait - /// implementation. + /// # Example: difference with [`Date::until`] + /// + /// The main difference between this routine and `Date::until` is that the + /// latter can return units other than a 96-bit integer of nanoseconds. + /// While a 96-bit integer of nanoseconds can be converted into other + /// units like hours, this can only be done for uniform units. (Uniform + /// units are units for which each individual unit always corresponds to + /// the same elapsed time regardless of the datetime it is relative to.) + /// This can't be done for units like years or months. /// /// ``` - /// use jiff::{civil::Date, Unit, ToSpan}; + /// use jiff::{civil::date, SignedDuration, Span, SpanRound, ToSpan, Unit}; /// - /// let d1 = Date::constant(1995, 12, 07); - /// let d2 = Date::constant(2019, 01, 31); + /// let d1 = date(2024, 1, 1); + /// let d2 = date(2025, 4, 1); /// - /// // The default limits durations to using "days" as the biggest unit. - /// let span = d1.until(d2)?; - /// assert_eq!(span.to_string(), "P8456d"); - /// - /// // But we can ask for units all the way up to years. /// let span = d1.until((Unit::Year, d2))?; - /// assert_eq!(span.to_string(), "P23y1m24d"); + /// assert_eq!(span, 1.year().months(3)); + /// + /// let duration = d1.duration_until(d2); + /// assert_eq!(duration, SignedDuration::from_hours(456 * 24)); + /// // There's no way to extract years or months from the signed + /// // duration like one might extract hours (because every hour + /// // is the same length). Instead, you actually have to convert + /// // it to a span and then balance it by providing a relative date! + /// let options = SpanRound::new().largest(Unit::Year).relative(d1); + /// let span = Span::try_from(duration)?.round(options)?; + /// assert_eq!(span, 1.year().months(3)); /// /// # Ok::<(), Box>(()) /// ``` /// - /// # Example: rounding the result + /// # Example: getting an unsigned duration /// - /// This shows how one might find the difference between two dates and - /// have the result rounded to the nearest month. - /// - /// In this case, we need to hand-construct a [`DateDifference`] - /// in order to gain full configurability. + /// If you're looking to find the duration between two dates as a + /// [`std::time::Duration`], you'll need to use this method to get a + /// [`SignedDuration`] and then convert it to a `std::time::Duration`: /// /// ``` - /// use jiff::{civil::{Date, DateDifference}, Unit, ToSpan}; + /// use std::time::Duration; /// - /// let d1 = Date::constant(1995, 12, 07); - /// let d2 = Date::constant(2019, 02, 06); + /// use jiff::{civil::date, SignedDuration}; /// - /// let span = d1.until(DateDifference::from(d2).smallest(Unit::Month))?; - /// assert_eq!(span, 277.months()); + /// let d1 = date(2024, 7, 1); + /// let d2 = date(2024, 8, 1); + /// let duration = Duration::try_from(d1.duration_until(d2))?; + /// assert_eq!(duration, Duration::from_secs(31 * 24 * 60 * 60)); /// - /// // Or even include years to make the span a bit more comprehensible. - /// let span = d1.until( - /// DateDifference::from(d2) - /// .smallest(Unit::Month) - /// .largest(Unit::Year), - /// )?; - /// // Notice that we are one day shy of 23y2m. Rounding spans computed - /// // between dates uses truncation by default. - /// assert_eq!(span, 23.years().months(1)); + /// // Note that unsigned durations cannot represent all + /// // possible differences! If the duration would be negative, + /// // then the conversion fails: + /// assert!(Duration::try_from(d2.duration_until(d1)).is_err()); /// /// # Ok::<(), Box>(()) /// ``` + #[inline] + pub fn duration_until(self, other: Date) -> SignedDuration { + SignedDuration::date_until(self, other) + } + + /// This routine is identical to [`Date::duration_until`], but the order of + /// the parameters is flipped. /// - /// # Example: units biggers than days inhibit reversibility - /// - /// If you ask for units bigger than days, then adding the span - /// returned to the `other` date is not guaranteed to result in the - /// original date. For example: + /// # Example /// /// ``` - /// use jiff::{civil::Date, Unit, ToSpan}; - /// - /// let d1 = Date::constant(2024, 3, 2); - /// let d2 = Date::constant(2024, 5, 1); - /// - /// let span = d1.until((Unit::Month, d2))?; - /// assert_eq!(span, 1.month().days(29)); - /// let maybe_original = d2.checked_sub(span)?; - /// // Not the same as the original datetime! - /// assert_eq!(maybe_original, Date::constant(2024, 3, 3)); - /// - /// // But in the default configuration, days are always the biggest unit - /// // and reversibility is guaranteed. - /// let span = d1.until(d2)?; - /// assert_eq!(span, 60.days()); - /// let is_original = d2.checked_sub(span)?; - /// assert_eq!(is_original, d1); + /// use jiff::{civil::date, SignedDuration}; /// - /// # Ok::<(), Box>(()) + /// let earlier = date(2006, 8, 24); + /// let later = date(2019, 1, 31); + /// assert_eq!( + /// later.duration_since(earlier), + /// SignedDuration::from_hours(4543 * 24), + /// ); /// ``` - /// - /// This occurs because spans are added as if by adding the biggest units - /// first, and then the smaller units. Because months vary in length, - /// their meaning can change depending on how the span is added. In this - /// case, adding one month to `2024-03-02` corresponds to 31 days, but - /// subtracting one month from `2024-05-01` corresponds to 30 days. #[inline] - pub fn until>( - self, - other: A, - ) -> Result { - let args: DateDifference = other.into(); - let span = args.until_with_largest_unit(self)?; - if args.rounding_may_change_span() { - span.round(args.round.relative(self)) - } else { - Ok(span) - } + pub fn duration_since(self, other: Date) -> SignedDuration { + SignedDuration::date_until(other, self) } /// Return an iterator of periodic dates determined by the given span. @@ -1967,9 +1884,9 @@ impl Date { /// 2020s. /// /// ``` - /// use jiff::{civil::{Date, Weekday}, ToSpan}; + /// use jiff::{civil::{Weekday, date}, ToSpan}; /// - /// let start = Date::constant(2020, 10, 31); + /// let start = date(2020, 10, 31); /// let mut halloween_days_of_week = vec![]; /// for halloween in start.series(1.years()).take(10) { /// halloween_days_of_week.push( @@ -1997,10 +1914,10 @@ impl Date { /// 2024? /// /// ``` - /// use jiff::{civil::Date, ToSpan}; + /// use jiff::{ToSpan, civil::date}; /// - /// let start = Date::constant(2024, 5, 1); - /// let end = Date::constant(2024, 10, 31); + /// let start = date(2024, 5, 1); + /// let end = date(2024, 10, 31); /// let mows = start /// .series(1.weeks().days(3).hours(12)) /// .take_while(|&d| d <= end) @@ -2014,19 +1931,19 @@ impl Date { /// granularity of a day, some dates may be repeated. /// /// ``` - /// use jiff::{civil::Date, ToSpan}; + /// use jiff::{civil::{Date, date}, ToSpan}; /// - /// let start = Date::constant(2024, 3, 11); + /// let start = date(2024, 3, 11); /// let every_five_hours: Vec = /// start.series(15.hours()).take(7).collect(); /// assert_eq!(every_five_hours, vec![ - /// Date::constant(2024, 3, 11), - /// Date::constant(2024, 3, 11), - /// Date::constant(2024, 3, 12), - /// Date::constant(2024, 3, 12), - /// Date::constant(2024, 3, 13), - /// Date::constant(2024, 3, 14), - /// Date::constant(2024, 3, 14), + /// date(2024, 3, 11), + /// date(2024, 3, 11), + /// date(2024, 3, 12), + /// date(2024, 3, 12), + /// date(2024, 3, 13), + /// date(2024, 3, 14), + /// date(2024, 3, 14), /// ]); /// ``` /// @@ -2035,9 +1952,9 @@ impl Date { /// When did the most recent Friday the 13th occur? /// /// ``` - /// use jiff::{civil::{Date, Weekday}, ToSpan}; + /// use jiff::{civil::{Weekday, date}, ToSpan}; /// - /// let start = Date::constant(2024, 3, 13); + /// let start = date(2024, 3, 13); /// let mut found = None; /// for date in start.series(-1.months()) { /// if date.weekday() == Weekday::Friday { @@ -2045,7 +1962,7 @@ impl Date { /// break; /// } /// } - /// assert_eq!(found, Some(Date::constant(2023, 10, 13))); + /// assert_eq!(found, Some(date(2023, 10, 13))); /// ``` #[inline] pub fn series(self, period: Span) -> DateSeries { @@ -2426,6 +2343,106 @@ impl core::ops::Sub for Date { } } +/// Adds a signed duration of time to a date. +/// +/// This uses checked arithmetic and panics on overflow. To handle overflow +/// without panics, use [`Date::checked_add`]. +impl core::ops::Add for Date { + type Output = Date; + + #[inline] + fn add(self, rhs: SignedDuration) -> Date { + self.checked_add(rhs) + .expect("adding signed duration to date overflowed") + } +} + +/// Adds a signed duration of time to a date in place. +/// +/// This uses checked arithmetic and panics on overflow. To handle overflow +/// without panics, use [`Date::checked_add`]. +impl core::ops::AddAssign for Date { + #[inline] + fn add_assign(&mut self, rhs: SignedDuration) { + *self = *self + rhs; + } +} + +/// Subtracts a signed duration of time from a date. +/// +/// This uses checked arithmetic and panics on overflow. To handle overflow +/// without panics, use [`Date::checked_sub`]. +impl core::ops::Sub for Date { + type Output = Date; + + #[inline] + fn sub(self, rhs: SignedDuration) -> Date { + self.checked_sub(rhs) + .expect("subing signed duration to date overflowed") + } +} + +/// Subtracts a signed duration of time from a date in place. +/// +/// This uses checked arithmetic and panics on overflow. To handle overflow +/// without panics, use [`Date::checked_sub`]. +impl core::ops::SubAssign for Date { + #[inline] + fn sub_assign(&mut self, rhs: SignedDuration) { + *self = *self - rhs; + } +} + +/// Adds an unsigned duration of time to a date. +/// +/// This uses checked arithmetic and panics on overflow. To handle overflow +/// without panics, use [`Date::checked_add`]. +impl core::ops::Add for Date { + type Output = Date; + + #[inline] + fn add(self, rhs: UnsignedDuration) -> Date { + self.checked_add(rhs) + .expect("adding unsigned duration to date overflowed") + } +} + +/// Adds an unsigned duration of time to a date in place. +/// +/// This uses checked arithmetic and panics on overflow. To handle overflow +/// without panics, use [`Date::checked_add`]. +impl core::ops::AddAssign for Date { + #[inline] + fn add_assign(&mut self, rhs: UnsignedDuration) { + *self = *self + rhs; + } +} + +/// Subtracts an unsigned duration of time from a date. +/// +/// This uses checked arithmetic and panics on overflow. To handle overflow +/// without panics, use [`Date::checked_sub`]. +impl core::ops::Sub for Date { + type Output = Date; + + #[inline] + fn sub(self, rhs: UnsignedDuration) -> Date { + self.checked_sub(rhs) + .expect("subing unsigned duration to date overflowed") + } +} + +/// Subtracts an unsigned duration of time from a date in place. +/// +/// This uses checked arithmetic and panics on overflow. To handle overflow +/// without panics, use [`Date::checked_sub`]. +impl core::ops::SubAssign for Date { + #[inline] + fn sub_assign(&mut self, rhs: UnsignedDuration) { + *self = *self - rhs; + } +} + #[cfg(feature = "serde")] impl serde::Serialize for Date { #[inline] @@ -2520,6 +2537,100 @@ impl Iterator for DateSeries { } } +/// Options for [`Date::checked_add`] and [`Date::checked_sub`]. +/// +/// This type provides a way to ergonomically add one of a few different +/// duration types to a [`Date`]. +/// +/// The main way to construct values of this type is with its `From` trait +/// implementations: +/// +/// * `From for DateArithmetic` adds (or subtracts) the given span to the +/// receiver date. +/// * `From for DateArithmetic` adds (or subtracts) +/// the given signed duration to the receiver date. +/// * `From for DateArithmetic` adds (or subtracts) +/// the given unsigned duration to the receiver date. +/// +/// # Example +/// +/// ``` +/// use std::time::Duration; +/// +/// use jiff::{civil::date, SignedDuration, ToSpan}; +/// +/// let d = date(2024, 2, 29); +/// assert_eq!(d.checked_add(1.year())?, date(2025, 2, 28)); +/// assert_eq!(d.checked_add(SignedDuration::from_hours(24))?, date(2024, 3, 1)); +/// assert_eq!(d.checked_add(Duration::from_secs(24 * 60 * 60))?, date(2024, 3, 1)); +/// +/// # Ok::<(), Box>(()) +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct DateArithmetic { + duration: Duration, +} + +impl DateArithmetic { + #[inline] + fn checked_add(self, date: Date) -> Result { + match self.duration.to_signed()? { + SDuration::Span(span) => date.checked_add_span(span), + SDuration::Absolute(sdur) => date.checked_add_duration(sdur), + } + } + + #[inline] + fn checked_neg(self) -> Result { + let duration = self.duration.checked_neg()?; + Ok(DateArithmetic { duration }) + } + + #[inline] + fn is_negative(&self) -> bool { + self.duration.is_negative() + } +} + +impl From for DateArithmetic { + fn from(span: Span) -> DateArithmetic { + let duration = Duration::from(span); + DateArithmetic { duration } + } +} + +impl From for DateArithmetic { + fn from(sdur: SignedDuration) -> DateArithmetic { + let duration = Duration::from(sdur); + DateArithmetic { duration } + } +} + +impl From for DateArithmetic { + fn from(udur: UnsignedDuration) -> DateArithmetic { + let duration = Duration::from(udur); + DateArithmetic { duration } + } +} + +impl<'a> From<&'a Span> for DateArithmetic { + fn from(span: &'a Span) -> DateArithmetic { + DateArithmetic::from(*span) + } +} + +impl<'a> From<&'a SignedDuration> for DateArithmetic { + fn from(sdur: &'a SignedDuration) -> DateArithmetic { + DateArithmetic::from(*sdur) + } +} + +impl<'a> From<&'a UnsignedDuration> for DateArithmetic { + fn from(udur: &'a UnsignedDuration) -> DateArithmetic { + DateArithmetic::from(*udur) + } +} + /// Options for [`Date::since`] and [`Date::until`]. /// /// This type provides a way to configure the calculation of spans between two @@ -3551,29 +3662,29 @@ mod tests { } assert_eq!( - Date::constant(1970, 1, 1), + date(1970, 1, 1), date_from_timestamp(Timestamp::new(0, 0).unwrap()), ); assert_eq!( - Date::constant(1969, 12, 31), + date(1969, 12, 31), date_from_timestamp(Timestamp::new(-1, 0).unwrap()), ); assert_eq!( - Date::constant(1969, 12, 31), + date(1969, 12, 31), date_from_timestamp(Timestamp::new(-86_400, 0).unwrap()), ); assert_eq!( - Date::constant(1969, 12, 30), + date(1969, 12, 30), date_from_timestamp(Timestamp::new(-86_401, 0).unwrap()), ); assert_eq!( - Date::constant(-9999, 1, 2), + date(-9999, 1, 2), date_from_timestamp( Timestamp::new(t::UnixSeconds::MIN_REPR, 0).unwrap() ), ); assert_eq!( - Date::constant(9999, 12, 30), + date(9999, 12, 30), date_from_timestamp( Timestamp::new(t::UnixSeconds::MAX_REPR, 0).unwrap() ), @@ -3600,7 +3711,7 @@ mod tests { for month in Month::MIN_REPR..=Month::MAX_REPR { let month = Month::new(month).unwrap(); for day in 1..=days_in_month(year, month).get() { - let date = Date::constant(year.get(), month.get(), day); + let date = date(year.get(), month.get(), day); let rd = date.to_unix_epoch_days(); let got = Date::from_unix_epoch_days(rd); assert_eq!(date, got, "for date {date:?}"); @@ -3619,7 +3730,7 @@ mod tests { for month in [1, 2, 4] { let month = Month::new(month).unwrap(); for day in 20..=days_in_month(year, month).get() { - let date = Date::constant(year.get(), month.get(), day); + let date = date(year.get(), month.get(), day); let wd = date.to_iso_week_date(); let got = wd.to_date(); assert_eq!( @@ -3635,29 +3746,29 @@ mod tests { fn add_constrained() { use crate::ToSpan; - let d1 = Date::constant(2023, 3, 31); + let d1 = date(2023, 3, 31); let d2 = d1.checked_add(1.months().days(1)).unwrap(); - assert_eq!(d2, Date::constant(2023, 5, 1)); + assert_eq!(d2, date(2023, 5, 1)); } #[test] fn since_years() { - let d1 = Date::constant(2023, 4, 15); - let d2 = Date::constant(2019, 2, 22); + let d1 = date(2023, 4, 15); + let d2 = date(2019, 2, 22); let span = d1.since((Unit::Year, d2)).unwrap(); assert_eq!(span, 4.years().months(1).days(21)); let span = d2.since((Unit::Year, d1)).unwrap(); assert_eq!(span, -4.years().months(1).days(24)); - let d1 = Date::constant(2023, 2, 22); - let d2 = Date::constant(2019, 4, 15); + let d1 = date(2023, 2, 22); + let d2 = date(2019, 4, 15); let span = d1.since((Unit::Year, d2)).unwrap(); assert_eq!(span, 3.years().months(10).days(7)); let span = d2.since((Unit::Year, d1)).unwrap(); assert_eq!(span, -3.years().months(10).days(7)); - let d1 = Date::constant(9999, 12, 31); - let d2 = Date::constant(-9999, 1, 1); + let d1 = date(9999, 12, 31); + let d2 = date(-9999, 1, 1); let span = d1.since((Unit::Year, d2)).unwrap(); assert_eq!(span, 19998.years().months(11).days(30)); let span = d2.since((Unit::Year, d1)).unwrap(); @@ -3666,8 +3777,8 @@ mod tests { #[test] fn since_months() { - let d1 = Date::constant(2024, 7, 24); - let d2 = Date::constant(2024, 2, 22); + let d1 = date(2024, 7, 24); + let d2 = date(2024, 2, 22); let span = d1.since((Unit::Month, d2)).unwrap(); assert_eq!(span, 5.months().days(2)); let span = d2.since((Unit::Month, d1)).unwrap(); @@ -3675,8 +3786,8 @@ mod tests { assert_eq!(d2, d1.checked_sub(5.months().days(2)).unwrap()); assert_eq!(d1, d2.checked_sub(-5.months().days(2)).unwrap()); - let d1 = Date::constant(2024, 7, 15); - let d2 = Date::constant(2024, 2, 22); + let d1 = date(2024, 7, 15); + let d2 = date(2024, 2, 22); let span = d1.since((Unit::Month, d2)).unwrap(); assert_eq!(span, 4.months().days(22)); let span = d2.since((Unit::Month, d1)).unwrap(); @@ -3684,8 +3795,8 @@ mod tests { assert_eq!(d2, d1.checked_sub(4.months().days(22)).unwrap()); assert_eq!(d1, d2.checked_sub(-4.months().days(23)).unwrap()); - let d1 = Date::constant(2023, 4, 15); - let d2 = Date::constant(2023, 2, 22); + let d1 = date(2023, 4, 15); + let d2 = date(2023, 2, 22); let span = d1.since((Unit::Month, d2)).unwrap(); assert_eq!(span, 1.month().days(21)); let span = d2.since((Unit::Month, d1)).unwrap(); @@ -3693,8 +3804,8 @@ mod tests { assert_eq!(d2, d1.checked_sub(1.month().days(21)).unwrap()); assert_eq!(d1, d2.checked_sub(-1.month().days(24)).unwrap()); - let d1 = Date::constant(2023, 4, 15); - let d2 = Date::constant(2019, 2, 22); + let d1 = date(2023, 4, 15); + let d2 = date(2019, 2, 22); let span = d1.since((Unit::Month, d2)).unwrap(); assert_eq!(span, 49.months().days(21)); let span = d2.since((Unit::Month, d1)).unwrap(); @@ -3703,8 +3814,8 @@ mod tests { #[test] fn since_weeks() { - let d1 = Date::constant(2024, 7, 15); - let d2 = Date::constant(2024, 6, 22); + let d1 = date(2024, 7, 15); + let d2 = date(2024, 6, 22); let span = d1.since((Unit::Week, d2)).unwrap(); assert_eq!(span, 3.weeks().days(2)); let span = d2.since((Unit::Week, d1)).unwrap(); @@ -3713,8 +3824,8 @@ mod tests { #[test] fn since_days() { - let d1 = Date::constant(2024, 7, 15); - let d2 = Date::constant(2024, 2, 22); + let d1 = date(2024, 7, 15); + let d2 = date(2024, 2, 22); let span = d1.since((Unit::Day, d2)).unwrap(); assert_eq!(span, 144.days()); let span = d2.since((Unit::Day, d1)).unwrap(); @@ -3723,9 +3834,9 @@ mod tests { #[test] fn until_month_lengths() { - let jan1 = Date::constant(2020, 1, 1); - let feb1 = Date::constant(2020, 2, 1); - let mar1 = Date::constant(2020, 3, 1); + let jan1 = date(2020, 1, 1); + let feb1 = date(2020, 2, 1); + let mar1 = date(2020, 3, 1); assert_eq!(jan1.until(feb1).unwrap(), 31.days()); assert_eq!(jan1.until((Unit::Month, feb1)).unwrap(), 1.month()); @@ -3742,8 +3853,8 @@ mod tests { // // => P2M // Temporal.PlainDate.from("2020-02-29").until("2020-04-30", {largestUnit: "months"}) // // => P2M1D - let d1 = Date::constant(2020, 4, 30); - let d2 = Date::constant(2020, 2, 29); + let d1 = date(2020, 4, 30); + let d2 = date(2020, 2, 29); let since = d1.since((Unit::Month, d2)).unwrap(); assert_eq!(since, 2.months()); @@ -3757,8 +3868,8 @@ mod tests { fn until_weeks_round() { use crate::{RoundMode, SpanRound}; - let earlier = Date::constant(2019, 1, 8); - let later = Date::constant(2021, 9, 7); + let earlier = date(2019, 1, 8); + let later = date(2021, 9, 7); let span = earlier.until((Unit::Week, later)).unwrap(); assert_eq!(span, 139.weeks()); diff --git a/src/civil/datetime.rs b/src/civil/datetime.rs index 327b68a3..07931f12 100644 --- a/src/civil/datetime.rs +++ b/src/civil/datetime.rs @@ -1,5 +1,8 @@ +use core::time::Duration as UnsignedDuration; + use crate::{ civil::{datetime, Date, DateWith, Era, Time, TimeWith, Weekday}, + duration::{Duration, SDuration}, error::{err, Error, ErrorContext}, fmt::{ self, @@ -12,7 +15,7 @@ use crate::{ t::{self, C}, }, zoned::Zoned, - RoundMode, Span, SpanRound, Unit, + RoundMode, SignedDuration, Span, SpanRound, Unit, }; /// A representation of a civil datetime in the Gregorian calendar. @@ -1429,6 +1432,10 @@ impl DateTime { /// Add the given span of time to this datetime. If the sum would overflow /// the minimum or maximum datetime values, then an error is returned. /// + /// This operation accepts three different duration types: [`Span`], + /// [`SignedDuration`] or [`std::time::Duration`]. This is achieved via + /// `From` trait implementations for the [`DateTimeArithmetic`] type. + /// /// # Properties /// /// This routine is _not_ reversible because some additions may @@ -1439,7 +1446,8 @@ impl DateTime { /// the date we started with. /// /// If spans of time are limited to units of days (or less), then this - /// routine _is_ reversible. + /// routine _is_ reversible. This also implies that all operations with a + /// [`SignedDuration`] or a [`std::time::Duration`] are reversible. /// /// # Errors /// @@ -1502,8 +1510,42 @@ impl DateTime { /// assert!(dt.checked_add(9000.years()).is_err()); /// assert!(dt.checked_add(-19000.years()).is_err()); /// ``` + /// + /// # Example: adding absolute durations + /// + /// This shows how to add signed and unsigned absolute durations to a + /// `DateTime`. + /// + /// ``` + /// use std::time::Duration; + /// + /// use jiff::{civil::date, SignedDuration}; + /// + /// let dt = date(2024, 2, 29).at(0, 0, 0, 0); + /// + /// let dur = SignedDuration::from_hours(25); + /// assert_eq!(dt.checked_add(dur)?, date(2024, 3, 1).at(1, 0, 0, 0)); + /// assert_eq!(dt.checked_add(-dur)?, date(2024, 2, 27).at(23, 0, 0, 0)); + /// + /// let dur = Duration::from_secs(25 * 60 * 60); + /// assert_eq!(dt.checked_add(dur)?, date(2024, 3, 1).at(1, 0, 0, 0)); + /// // One cannot negate an unsigned duration, + /// // but you can subtract it! + /// assert_eq!(dt.checked_sub(dur)?, date(2024, 2, 27).at(23, 0, 0, 0)); + /// + /// # Ok::<(), Box>(()) + /// ``` + #[inline] + pub fn checked_add>( + self, + duration: A, + ) -> Result { + let duration: DateTimeArithmetic = duration.into(); + duration.checked_add(self) + } + #[inline] - pub fn checked_add(self, span: Span) -> Result { + fn checked_add_span(self, span: Span) -> Result { let (date, time) = (self.date(), self.time()); let span_date = span.without_lower(Unit::Day); let span_time = span.only_lower(Unit::Day); @@ -1524,146 +1566,87 @@ impl DateTime { Ok(DateTime::from_parts(new_date, new_time)) } - /// Subtract the given span of time from this datetime. If the difference - /// would overflow the minimum or maximum datetime values, then an error is - /// returned. - /// - /// # Properties - /// - /// This routine is _not_ reversible because some additions may - /// be ambiguous. For example, adding `1 month` to the datetime - /// `2024-03-31T00:00:00` will produce `2024-04-30T00:00:00` since April - /// has only 30 days in a month. Moreover, subtracting `1 month` from - /// `2024-04-30T00:00:00` will produce `2024-03-30T00:00:00`, which is not - /// the date we started with. - /// - /// If spans of time are limited to units of days (or less), then this - /// routine _is_ reversible. + #[inline] + fn checked_add_duration( + self, + duration: SignedDuration, + ) -> Result { + let (date, time) = (self.date(), self.time()); + let (new_time, leftovers) = time.overflowing_add_duration(duration)?; + let new_date = date.checked_add(leftovers).with_context(|| { + err!( + "failed to add overflowing signed duration, {leftovers:?}, \ + from adding {duration:?} to {time}, + to {date}", + ) + })?; + Ok(DateTime::from_parts(new_date, new_time)) + } + + /// This routine is identical to [`DateTime::checked_add`] with the + /// duration negated. /// /// # Errors /// - /// If the span subtracted from this datetime would result in a datetime - /// that exceeds the range of a `DateTime`, then this will return an error. + /// This has the same error conditions as [`DateTime::checked_add`]. /// /// # Example /// - /// This shows a few examples of subtracting spans of time to various - /// dates. We make use of the [`ToSpan`](crate::ToSpan) trait for - /// convenient creation of spans. - /// - /// ``` - /// use jiff::{civil::date, ToSpan}; - /// - /// let dt = date(1995, 12, 7).at(3, 24, 30, 3_500); - /// let got = dt.checked_sub(20.years().months(4).nanoseconds(500))?; - /// assert_eq!(got, date(1975, 8, 7).at(3, 24, 30, 3_000)); - /// - /// let dt = date(2019, 2, 28).at(15, 30, 0, 0); - /// let got = dt.checked_sub(1.months())?; - /// assert_eq!(got, date(2019, 1, 28).at(15, 30, 0, 0)); - /// - /// # Ok::<(), Box>(()) - /// ``` - /// - /// # Example: available via subtraction operator - /// /// This routine can be used via the `-` operator. Note though that if it /// fails, it will result in a panic. /// /// ``` - /// use jiff::{civil::date, ToSpan}; - /// - /// let dt = date(1995, 12, 7).at(3, 24, 30, 3_500); - /// let got = dt - 20.years().months(4).nanoseconds(500); - /// assert_eq!(got, date(1975, 8, 7).at(3, 24, 30, 3_000)); - /// ``` + /// use std::time::Duration; /// - /// # Example: negative spans are supported - /// - /// ``` - /// use jiff::{civil::date, ToSpan}; + /// use jiff::{civil::date, SignedDuration, ToSpan}; /// - /// let dt = date(2024, 3, 31).at(19, 5, 59, 999_999_999); + /// let dt = date(1995, 12, 7).at(3, 24, 30, 3_500); /// assert_eq!( - /// dt.checked_sub(-1.months())?, - /// date(2024, 4, 30).at(19, 5, 59, 999_999_999), + /// dt - 20.years().months(4).nanoseconds(500), + /// date(1975, 8, 7).at(3, 24, 30, 3_000), /// ); /// - /// # Ok::<(), Box>(()) - /// ``` - /// - /// # Example: error on overflow + /// let dur = SignedDuration::new(24 * 60 * 60, 3_500); + /// assert_eq!(dt - dur, date(1995, 12, 6).at(3, 24, 30, 0)); /// - /// ``` - /// use jiff::{civil::date, ToSpan}; + /// let dur = Duration::new(24 * 60 * 60, 3_500); + /// assert_eq!(dt - dur, date(1995, 12, 6).at(3, 24, 30, 0)); /// - /// let dt = date(2024, 3, 31).at(13, 13, 13, 13); - /// assert!(dt.checked_sub(19000.years()).is_err()); - /// assert!(dt.checked_sub(-9000.years()).is_err()); + /// # Ok::<(), Box>(()) /// ``` #[inline] - pub fn checked_sub(self, span: Span) -> Result { - self.checked_add(-span) + pub fn checked_sub>( + self, + duration: A, + ) -> Result { + let duration: DateTimeArithmetic = duration.into(); + duration.checked_neg().and_then(|dta| dta.checked_add(self)) } - /// Add the given span of time to this datetime. If the sum would overflow - /// the minimum or maximum datetime values, then the result saturates. - /// - /// # Properties - /// - /// This routine is _not_ reversible because some additions may - /// be ambiguous. For example, adding `1 month` to the datetime - /// `2024-03-31T00:00:00` will produce `2024-04-30T00:00:00` since April - /// has only 30 days in a month. Moreover, subtracting `1 month` from - /// `2024-04-30T00:00:00` will produce `2024-03-30T00:00:00`, which is not - /// the date we started with. - /// - /// If spans of time are limited to units of days (or less), and no - /// saturation occurs, then this routine _is_ reversible. + /// This routine is identical to [`DateTime::checked_add`], except the + /// result saturates on overflow. That is, instead of overflow, either + /// [`DateTime::MIN`] or [`DateTime::MAX`] is returned. /// /// # Example /// - /// This shows a few examples of adding spans of time to various dates. - /// We make use of the [`ToSpan`](crate::ToSpan) trait for convenient - /// creation of spans. - /// - /// ``` - /// use jiff::{civil::date, ToSpan}; - /// - /// let dt = date(1995, 12, 7).at(3, 24, 30, 3_500); - /// let got = dt.saturating_add(20.years().months(4).nanoseconds(500)); - /// assert_eq!(got, date(2016, 4, 7).at(3, 24, 30, 4_000)); - /// - /// let dt = date(2019, 1, 31).at(15, 30, 0, 0); - /// let got = dt.saturating_add(1.months()); - /// assert_eq!(got, date(2019, 2, 28).at(15, 30, 0, 0)); /// ``` - /// - /// # Example: negative spans are supported - /// - /// ``` - /// use jiff::{civil::date, ToSpan}; - /// - /// let dt = date(2024, 3, 31).at(19, 5, 59, 999_999_999); - /// assert_eq!( - /// dt.saturating_add(-1.months()), - /// date(2024, 2, 29).at(19, 5, 59, 999_999_999), - /// ); - /// ``` - /// - /// # Example: saturation on overflow - /// - /// ``` - /// use jiff::{civil::{DateTime, date}, ToSpan}; + /// use jiff::{civil::{DateTime, date}, SignedDuration, ToSpan}; /// /// let dt = date(2024, 3, 31).at(13, 13, 13, 13); /// assert_eq!(DateTime::MAX, dt.saturating_add(9000.years())); /// assert_eq!(DateTime::MIN, dt.saturating_add(-19000.years())); + /// assert_eq!(DateTime::MAX, dt.saturating_add(SignedDuration::MAX)); + /// assert_eq!(DateTime::MIN, dt.saturating_add(SignedDuration::MIN)); + /// assert_eq!(DateTime::MAX, dt.saturating_add(std::time::Duration::MAX)); /// ``` #[inline] - pub fn saturating_add(self, span: Span) -> DateTime { - self.checked_add(span).unwrap_or_else(|_| { - if span.is_negative() { + pub fn saturating_add>( + self, + duration: A, + ) -> DateTime { + let duration: DateTimeArithmetic = duration.into(); + self.checked_add(duration).unwrap_or_else(|_| { + if duration.is_negative() { DateTime::MIN } else { DateTime::MAX @@ -1671,70 +1654,37 @@ impl DateTime { }) } - /// Subtract the given span of time from this datetime. If the difference - /// would overflow the minimum or maximum datetime values, then the result - /// saturates. - /// - /// # Properties - /// - /// This routine is _not_ reversible because some additions may - /// be ambiguous. For example, adding `1 month` to the datetime - /// `2024-03-31T00:00:00` will produce `2024-04-30T00:00:00` since April - /// has only 30 days in a month. Moreover, subtracting `1 month` from - /// `2024-04-30T00:00:00` will produce `2024-03-30T00:00:00`, which is not - /// the date we started with. - /// - /// If spans of time are limited to units of days (or less), and no - /// saturation occurs, then this routine _is_ reversible. + /// This routine is identical to [`DateTime::saturating_add`] with the span + /// parameter negated. /// /// # Example /// - /// This shows a few examples of subtracting spans of time to various - /// dates. We make use of the [`ToSpan`](crate::ToSpan) trait for - /// convenient creation of spans. - /// /// ``` - /// use jiff::{civil::date, ToSpan}; - /// - /// let dt = date(1995, 12, 7).at(3, 24, 30, 3_500); - /// let got = dt.saturating_sub(20.years().months(4).nanoseconds(500)); - /// assert_eq!(got, date(1975, 8, 7).at(3, 24, 30, 3_000)); - /// - /// let dt = date(2019, 2, 28).at(15, 30, 0, 0); - /// let got = dt.saturating_sub(1.months()); - /// assert_eq!(got, date(2019, 1, 28).at(15, 30, 0, 0)); - /// ``` - /// - /// # Example: negative spans are supported - /// - /// ``` - /// use jiff::{civil::date, ToSpan}; - /// - /// let dt = date(2024, 3, 31).at(19, 5, 59, 999_999_999); - /// assert_eq!( - /// dt.saturating_sub(-1.months()), - /// date(2024, 4, 30).at(19, 5, 59, 999_999_999), - /// ); - /// ``` - /// - /// # Example: saturation on overflow - /// - /// ``` - /// use jiff::{civil::{DateTime, date}, ToSpan}; + /// use jiff::{civil::{DateTime, date}, SignedDuration, ToSpan}; /// /// let dt = date(2024, 3, 31).at(13, 13, 13, 13); /// assert_eq!(DateTime::MIN, dt.saturating_sub(19000.years())); /// assert_eq!(DateTime::MAX, dt.saturating_sub(-9000.years())); + /// assert_eq!(DateTime::MIN, dt.saturating_sub(SignedDuration::MAX)); + /// assert_eq!(DateTime::MAX, dt.saturating_sub(SignedDuration::MIN)); + /// assert_eq!(DateTime::MIN, dt.saturating_sub(std::time::Duration::MAX)); /// ``` #[inline] - pub fn saturating_sub(self, span: Span) -> DateTime { - self.saturating_add(-span) + pub fn saturating_sub>( + self, + duration: A, + ) -> DateTime { + let duration: DateTimeArithmetic = duration.into(); + let Ok(duration) = duration.checked_neg() else { + return DateTime::MIN; + }; + self.saturating_add(duration) } - /// Returns a span representing the elapsed time from this datetime since + /// Returns a span representing the elapsed time from this datetime until /// the given `other` datetime. /// - /// When `other` occurs after this datetime, then the span returned will + /// When `other` occurs before this datetime, then the span returned will /// be negative. /// /// Depending on the input provided, the span returned is rounded. It may @@ -1749,29 +1699,26 @@ impl DateTime { /// /// # Properties /// - /// It is guaranteed that if the returned span is added to `other`, and if - /// no rounding is requested, and if the largest unit requested is at most - /// `Unit::Day`, then the original datetime will be returned. + /// It is guaranteed that if the returned span is subtracted from `other`, + /// and if no rounding is requested, and if the largest unit requested is + /// at most `Unit::Day`, then the original datetime will be returned. /// - /// This routine is equivalent to `self.until(other).map(|span| -span)` + /// This routine is equivalent to `self.since(other).map(|span| -span)` /// if no rounding options are set. If rounding options are set, then /// it's equivalent to - /// `self.until(other_without_rounding_options).map(|span| -span)`, + /// `self.since(other_without_rounding_options).map(|span| -span)`, /// followed by a call to [`Span::round`] with the appropriate rounding /// options set. This is because the negation of a span can result in /// different rounding results depending on the rounding mode. /// /// # Errors /// - /// An error can occur in some cases when the requested configuration - /// would result in a span that is beyond allowable limits. For example, - /// the nanosecond component of a span cannot represent the span of - /// time between the minimum and maximum datetime supported by Jiff. - /// Therefore, if one requests a span with its largest unit set to - /// [`Unit::Nanosecond`], then it's possible for this routine to fail. - /// - /// An error can also occur if `DateTimeDifference` is misconfigured. For - /// example, if the smallest unit provided is bigger than the largest unit. + /// An error can occur in some cases when the requested configuration would + /// result in a span that is beyond allowable limits. For example, the + /// nanosecond component of a span cannot the span of time between the + /// minimum and maximum datetime supported by Jiff. Therefore, if one + /// requests a span with its largest unit set to [`Unit::Nanosecond`], then + /// it's possible for this routine to fail. /// /// It is guaranteed that if one provides a datetime with the default /// [`DateTimeDifference`] configuration, then this routine will never @@ -1784,28 +1731,14 @@ impl DateTime { /// /// let earlier = date(2006, 8, 24).at(22, 30, 0, 0); /// let later = date(2019, 1, 31).at(21, 0, 0, 0); - /// assert_eq!(later.since(earlier)?, 4542.days().hours(22).minutes(30)); + /// assert_eq!(earlier.until(later)?, 4542.days().hours(22).minutes(30)); /// /// // Flipping the dates is fine, but you'll get a negative span. - /// assert_eq!(earlier.since(later)?, -4542.days().hours(22).minutes(30)); + /// assert_eq!(later.until(earlier)?, -4542.days().hours(22).minutes(30)); /// /// # Ok::<(), Box>(()) /// ``` /// - /// # Example: available via subtraction operator - /// - /// This routine can be used via the `-` operator. Since the default - /// configuration is used and because a `Span` can represent the difference - /// between any two possible datetimes, it will never panic. - /// - /// ``` - /// use jiff::{civil::date, ToSpan}; - /// - /// let earlier = date(2006, 8, 24).at(22, 30, 0, 0); - /// let later = date(2019, 1, 31).at(21, 0, 0, 0); - /// assert_eq!(later - earlier, 4542.days().hours(22).minutes(30)); - /// ``` - /// /// # Example: using bigger units /// /// This example shows how to expand the span returned to bigger units. @@ -1819,11 +1752,11 @@ impl DateTime { /// let dt2 = date(2019, 01, 31).at(15, 30, 0, 0); /// /// // The default limits durations to using "days" as the biggest unit. - /// let span = dt2.since(dt1)?; + /// let span = dt1.until(dt2)?; /// assert_eq!(span.to_string(), "P8456dT12h5m29.9999965s"); /// /// // But we can ask for units all the way up to years. - /// let span = dt2.since((Unit::Year, dt1))?; + /// let span = dt1.until((Unit::Year, dt2))?; /// assert_eq!(span.to_string(), "P23y1m24dT12h5m29.9999965s"); /// # Ok::<(), Box>(()) /// ``` @@ -1842,14 +1775,14 @@ impl DateTime { /// let dt1 = date(1995, 12, 07).at(3, 24, 30, 3500); /// let dt2 = date(2019, 01, 31).at(15, 30, 0, 0); /// - /// let span = dt2.since( - /// DateTimeDifference::from(dt1).smallest(Unit::Second), + /// let span = dt1.until( + /// DateTimeDifference::from(dt2).smallest(Unit::Second), /// )?; /// assert_eq!(span, 8456.days().hours(12).minutes(5).seconds(29)); /// /// // We can combine smallest and largest units too! - /// let span = dt2.since( - /// DateTimeDifference::from(dt1) + /// let span = dt1.until( + /// DateTimeDifference::from(dt2) /// .smallest(Unit::Second) /// .largest(Unit::Year), /// )?; @@ -1859,8 +1792,8 @@ impl DateTime { /// /// # Example: units biggers than days inhibit reversibility /// - /// If you ask for units bigger than days, then adding the span - /// returned to the `other` datetime is not guaranteed to result in the + /// If you ask for units bigger than days, then subtracting the span + /// returned from the `other` datetime is not guaranteed to result in the /// original datetime. For example: /// /// ``` @@ -1869,34 +1802,34 @@ impl DateTime { /// let dt1 = date(2024, 3, 2).at(0, 0, 0, 0); /// let dt2 = date(2024, 5, 1).at(0, 0, 0, 0); /// - /// let span = dt2.since((Unit::Month, dt1))?; - /// assert_eq!(span, 1.month().days(30)); - /// let maybe_original = dt1.checked_add(span)?; + /// let span = dt1.until((Unit::Month, dt2))?; + /// assert_eq!(span, 1.month().days(29)); + /// let maybe_original = dt2.checked_sub(span)?; /// // Not the same as the original datetime! - /// assert_eq!(maybe_original, date(2024, 5, 2).at(0, 0, 0, 0)); + /// assert_eq!(maybe_original, date(2024, 3, 3).at(0, 0, 0, 0)); /// /// // But in the default configuration, days are always the biggest unit /// // and reversibility is guaranteed. - /// let span = dt2.since(dt1)?; + /// let span = dt1.until(dt2)?; /// assert_eq!(span, 60.days()); - /// let is_original = dt1.checked_add(span)?; - /// assert_eq!(is_original, dt2); + /// let is_original = dt2.checked_sub(span)?; + /// assert_eq!(is_original, dt1); /// /// # Ok::<(), Box>(()) /// ``` /// - /// This occurs because spans are added as if by adding the biggest units + /// This occurs because span are added as if by adding the biggest units /// first, and then the smaller units. Because months vary in length, /// their meaning can change depending on how the span is added. In this /// case, adding one month to `2024-03-02` corresponds to 31 days, but /// subtracting one month from `2024-05-01` corresponds to 30 days. #[inline] - pub fn since>( + pub fn until>( self, other: A, ) -> Result { let args: DateTimeDifference = other.into(); - let span = -args.until_with_largest_unit(self)?; + let span = args.until_with_largest_unit(self)?; if args.rounding_may_change_span() { span.round(args.round.relative(self)) } else { @@ -1904,160 +1837,167 @@ impl DateTime { } } - /// Returns a span representing the elapsed time from this datetime until - /// the given `other` datetime. - /// - /// When `other` occurs before this datetime, then the span returned will - /// be negative. - /// - /// Depending on the input provided, the span returned is rounded. It may - /// also be balanced up to bigger units than the default. By default, the - /// span returned is balanced such that the biggest possible unit is days. - /// - /// This operation is configured by providing a [`DateTimeDifference`] - /// value. Since this routine accepts anything that implements - /// `Into`, once can pass a `DateTime` directly. - /// One can also pass a `(Unit, DateTime)`, where `Unit` is treated as - /// [`DateTimeDifference::largest`]. - /// - /// # Properties - /// - /// It is guaranteed that if the returned span is subtracted from `other`, - /// and if no rounding is requested, and if the largest unit requested is - /// at most `Unit::Day`, then the original datetime will be returned. - /// - /// This routine is equivalent to `self.since(other).map(|span| -span)` - /// if no rounding options are set. If rounding options are set, then - /// it's equivalent to - /// `self.since(other_without_rounding_options).map(|span| -span)`, - /// followed by a call to [`Span::round`] with the appropriate rounding - /// options set. This is because the negation of a span can result in - /// different rounding results depending on the rounding mode. + /// This routine is identical to [`DateTime::until`], but the order of the + /// parameters is flipped. /// /// # Errors /// - /// An error can occur in some cases when the requested configuration would - /// result in a span that is beyond allowable limits. For example, the - /// nanosecond component of a span cannot the span of time between the - /// minimum and maximum datetime supported by Jiff. Therefore, if one - /// requests a span with its largest unit set to [`Unit::Nanosecond`], then - /// it's possible for this routine to fail. - /// - /// It is guaranteed that if one provides a datetime with the default - /// [`DateTimeDifference`] configuration, then this routine will never - /// fail. + /// This has the same error conditions as [`DateTime::until`]. /// /// # Example /// + /// This routine can be used via the `-` operator. Since the default + /// configuration is used and because a `Span` can represent the difference + /// between any two possible datetimes, it will never panic. + /// /// ``` /// use jiff::{civil::date, ToSpan}; /// /// let earlier = date(2006, 8, 24).at(22, 30, 0, 0); /// let later = date(2019, 1, 31).at(21, 0, 0, 0); - /// assert_eq!(earlier.until(later)?, 4542.days().hours(22).minutes(30)); + /// assert_eq!(later - earlier, 4542.days().hours(22).minutes(30)); + /// ``` + #[inline] + pub fn since>( + self, + other: A, + ) -> Result { + let args: DateTimeDifference = other.into(); + let span = -args.until_with_largest_unit(self)?; + if args.rounding_may_change_span() { + span.round(args.round.relative(self)) + } else { + Ok(span) + } + } + + /// Returns an absolute duration representing the elapsed time from this + /// datetime until the given `other` datetime. /// - /// // Flipping the dates is fine, but you'll get a negative span. - /// assert_eq!(later.until(earlier)?, -4542.days().hours(22).minutes(30)); + /// When `other` occurs before this datetime, then the duration returned + /// will be negative. /// - /// # Ok::<(), Box>(()) - /// ``` + /// Unlike [`DateTime::until`], this returns a duration corresponding to a + /// 96-bit integer of nanoseconds between two datetimes. /// - /// # Example: using bigger units + /// # Fallibility /// - /// This example shows how to expand the span returned to bigger units. - /// This makes use of a `From<(Unit, DateTime)> for DateTimeDifference` - /// trait implementation. + /// This routine never panics or returns an error. Since there are no + /// configuration options that can be incorrectly provided, no error is + /// possible when calling this routine. In contrast, [`DateTime::until`] + /// can return an error in some cases due to misconfiguration. But like + /// this routine, [`DateTime::until`] never panics or returns an error in + /// its default configuration. /// - /// ``` - /// use jiff::{civil::date, Unit, ToSpan}; + /// # When should I use this versus [`DateTime::until`]? /// - /// let dt1 = date(1995, 12, 07).at(3, 24, 30, 3500); - /// let dt2 = date(2019, 01, 31).at(15, 30, 0, 0); + /// See the type documentation for [`SignedDuration`] for the section on + /// when one should use [`Span`] and when one should use `SignedDuration`. + /// In short, use `Span` (and therefore `DateTime::until`) unless you have + /// a specific reason to do otherwise. /// - /// // The default limits durations to using "days" as the biggest unit. - /// let span = dt1.until(dt2)?; - /// assert_eq!(span.to_string(), "P8456dT12h5m29.9999965s"); + /// # Example /// - /// // But we can ask for units all the way up to years. - /// let span = dt1.until((Unit::Year, dt2))?; - /// assert_eq!(span.to_string(), "P23y1m24dT12h5m29.9999965s"); - /// # Ok::<(), Box>(()) /// ``` + /// use jiff::{civil::date, SignedDuration}; /// - /// # Example: rounding the result + /// let earlier = date(2006, 8, 24).at(22, 30, 0, 0); + /// let later = date(2019, 1, 31).at(21, 0, 0, 0); + /// assert_eq!( + /// earlier.duration_until(later), + /// SignedDuration::from_hours(4542 * 24) + /// + SignedDuration::from_hours(22) + /// + SignedDuration::from_mins(30), + /// ); + /// // Flipping the datetimes is fine, but you'll get a negative duration. + /// assert_eq!( + /// later.duration_until(earlier), + /// -SignedDuration::from_hours(4542 * 24) + /// - SignedDuration::from_hours(22) + /// - SignedDuration::from_mins(30), + /// ); + /// ``` /// - /// This shows how one might find the difference between two datetimes and - /// have the result rounded such that sub-seconds are removed. + /// # Example: difference with [`DateTime::until`] /// - /// In this case, we need to hand-construct a [`DateTimeDifference`] - /// in order to gain full configurability. + /// The main difference between this routine and `DateTime::until` is that + /// the latter can return units other than a 96-bit integer of nanoseconds. + /// While a 96-bit integer of nanoseconds can be converted into other units + /// like hours, this can only be done for uniform units. (Uniform units are + /// units for which each individual unit always corresponds to the same + /// elapsed time regardless of the datetime it is relative to.) This can't + /// be done for units like years or months. /// /// ``` - /// use jiff::{civil::{DateTimeDifference, date}, Unit, ToSpan}; + /// use jiff::{civil::date, SignedDuration, Span, SpanRound, ToSpan, Unit}; /// - /// let dt1 = date(1995, 12, 07).at(3, 24, 30, 3500); - /// let dt2 = date(2019, 01, 31).at(15, 30, 0, 0); + /// let dt1 = date(2024, 1, 1).at(0, 0, 0, 0); + /// let dt2 = date(2025, 4, 1).at(0, 0, 0, 0); /// - /// let span = dt1.until( - /// DateTimeDifference::from(dt2).smallest(Unit::Second), - /// )?; - /// assert_eq!(span, 8456.days().hours(12).minutes(5).seconds(29)); + /// let span = dt1.until((Unit::Year, dt2))?; + /// assert_eq!(span, 1.year().months(3)); + /// + /// let duration = dt1.duration_until(dt2); + /// assert_eq!(duration, SignedDuration::from_hours(456 * 24)); + /// // There's no way to extract years or months from the signed + /// // duration like one might extract hours (because every hour + /// // is the same length). Instead, you actually have to convert + /// // it to a span and then balance it by providing a relative date! + /// let options = SpanRound::new().largest(Unit::Year).relative(dt1); + /// let span = Span::try_from(duration)?.round(options)?; + /// assert_eq!(span, 1.year().months(3)); /// - /// // We can combine smallest and largest units too! - /// let span = dt1.until( - /// DateTimeDifference::from(dt2) - /// .smallest(Unit::Second) - /// .largest(Unit::Year), - /// )?; - /// assert_eq!(span, 23.years().months(1).days(24).hours(12).minutes(5).seconds(29)); /// # Ok::<(), Box>(()) /// ``` /// - /// # Example: units biggers than days inhibit reversibility + /// # Example: getting an unsigned duration /// - /// If you ask for units bigger than days, then subtracting the span - /// returned from the `other` datetime is not guaranteed to result in the - /// original datetime. For example: + /// If you're looking to find the duration between two datetimes as a + /// [`std::time::Duration`], you'll need to use this method to get a + /// [`SignedDuration`] and then convert it to a `std::time::Duration`: /// /// ``` - /// use jiff::{civil::date, Unit, ToSpan}; + /// use std::time::Duration; /// - /// let dt1 = date(2024, 3, 2).at(0, 0, 0, 0); - /// let dt2 = date(2024, 5, 1).at(0, 0, 0, 0); + /// use jiff::civil::date; /// - /// let span = dt1.until((Unit::Month, dt2))?; - /// assert_eq!(span, 1.month().days(29)); - /// let maybe_original = dt2.checked_sub(span)?; - /// // Not the same as the original datetime! - /// assert_eq!(maybe_original, date(2024, 3, 3).at(0, 0, 0, 0)); + /// let dt1 = date(2024, 7, 1).at(0, 0, 0, 0); + /// let dt2 = date(2024, 8, 1).at(0, 0, 0, 0); + /// let duration = Duration::try_from(dt1.duration_until(dt2))?; + /// assert_eq!(duration, Duration::from_secs(31 * 24 * 60 * 60)); /// - /// // But in the default configuration, days are always the biggest unit - /// // and reversibility is guaranteed. - /// let span = dt1.until(dt2)?; - /// assert_eq!(span, 60.days()); - /// let is_original = dt2.checked_sub(span)?; - /// assert_eq!(is_original, dt1); + /// // Note that unsigned durations cannot represent all + /// // possible differences! If the duration would be negative, + /// // then the conversion fails: + /// assert!(Duration::try_from(dt2.duration_until(dt1)).is_err()); /// /// # Ok::<(), Box>(()) /// ``` + #[inline] + pub fn duration_until(self, other: DateTime) -> SignedDuration { + SignedDuration::datetime_until(self, other) + } + + /// This routine is identical to [`DateTime::duration_until`], but the + /// order of the parameters is flipped. /// - /// This occurs because span are added as if by adding the biggest units - /// first, and then the smaller units. Because months vary in length, - /// their meaning can change depending on how the span is added. In this - /// case, adding one month to `2024-03-02` corresponds to 31 days, but - /// subtracting one month from `2024-05-01` corresponds to 30 days. + /// # Example + /// + /// ``` + /// use jiff::{civil::date, SignedDuration}; + /// + /// let earlier = date(2006, 8, 24).at(22, 30, 0, 0); + /// let later = date(2019, 1, 31).at(21, 0, 0, 0); + /// assert_eq!( + /// later.duration_since(earlier), + /// SignedDuration::from_hours(4542 * 24) + /// + SignedDuration::from_hours(22) + /// + SignedDuration::from_mins(30), + /// ); + /// ``` #[inline] - pub fn until>( - self, - other: A, - ) -> Result { - let args: DateTimeDifference = other.into(); - let span = args.until_with_largest_unit(self)?; - if args.rounding_may_change_span() { - span.round(args.round.relative(self)) - } else { - Ok(span) - } + pub fn duration_since(self, other: DateTime) -> SignedDuration { + SignedDuration::datetime_until(other, self) } /// Rounds this datetime according to the [`DateTimeRound`] configuration @@ -2200,9 +2140,9 @@ impl DateTime { /// # Ok::<(), Box>(()) /// ``` #[inline] - pub fn round( + pub fn round>( self, - options: impl Into, + options: R, ) -> Result { let options: DateTimeRound = options.into(); options.round(t::NANOS_PER_CIVIL_DAY, self) @@ -2452,6 +2392,9 @@ impl core::ops::SubAssign for DateTime { /// panic or fail in any way. /// /// To configure the largest unit or enable rounding, use [`DateTime::since`]. +/// +/// If you need a [`SignedDuration`] representing the span between two civil +/// datetimes, then use [`DateTime::duration_since`]. impl core::ops::Sub for DateTime { type Output = Span; @@ -2461,6 +2404,106 @@ impl core::ops::Sub for DateTime { } } +/// Adds a signed duration of time to a datetime. +/// +/// This uses checked arithmetic and panics on overflow. To handle overflow +/// without panics, use [`DateTime::checked_add`]. +impl core::ops::Add for DateTime { + type Output = DateTime; + + #[inline] + fn add(self, rhs: SignedDuration) -> DateTime { + self.checked_add(rhs) + .expect("adding signed duration to datetime overflowed") + } +} + +/// Adds a signed duration of time to a datetime in place. +/// +/// This uses checked arithmetic and panics on overflow. To handle overflow +/// without panics, use [`DateTime::checked_add`]. +impl core::ops::AddAssign for DateTime { + #[inline] + fn add_assign(&mut self, rhs: SignedDuration) { + *self = *self + rhs + } +} + +/// Subtracts a signed duration of time from a datetime. +/// +/// This uses checked arithmetic and panics on overflow. To handle overflow +/// without panics, use [`DateTime::checked_sub`]. +impl core::ops::Sub for DateTime { + type Output = DateTime; + + #[inline] + fn sub(self, rhs: SignedDuration) -> DateTime { + self.checked_sub(rhs) + .expect("subtracting signed duration from datetime overflowed") + } +} + +/// Subtracts a signed duration of time from a datetime in place. +/// +/// This uses checked arithmetic and panics on overflow. To handle overflow +/// without panics, use [`DateTime::checked_sub`]. +impl core::ops::SubAssign for DateTime { + #[inline] + fn sub_assign(&mut self, rhs: SignedDuration) { + *self = *self - rhs + } +} + +/// Adds an unsigned duration of time to a datetime. +/// +/// This uses checked arithmetic and panics on overflow. To handle overflow +/// without panics, use [`DateTime::checked_add`]. +impl core::ops::Add for DateTime { + type Output = DateTime; + + #[inline] + fn add(self, rhs: UnsignedDuration) -> DateTime { + self.checked_add(rhs) + .expect("adding unsigned duration to datetime overflowed") + } +} + +/// Adds an unsigned duration of time to a datetime in place. +/// +/// This uses checked arithmetic and panics on overflow. To handle overflow +/// without panics, use [`DateTime::checked_add`]. +impl core::ops::AddAssign for DateTime { + #[inline] + fn add_assign(&mut self, rhs: UnsignedDuration) { + *self = *self + rhs + } +} + +/// Subtracts an unsigned duration of time from a datetime. +/// +/// This uses checked arithmetic and panics on overflow. To handle overflow +/// without panics, use [`DateTime::checked_sub`]. +impl core::ops::Sub for DateTime { + type Output = DateTime; + + #[inline] + fn sub(self, rhs: UnsignedDuration) -> DateTime { + self.checked_sub(rhs) + .expect("subtracting unsigned duration from datetime overflowed") + } +} + +/// Subtracts an unsigned duration of time from a datetime in place. +/// +/// This uses checked arithmetic and panics on overflow. To handle overflow +/// without panics, use [`DateTime::checked_sub`]. +impl core::ops::SubAssign for DateTime { + #[inline] + fn sub_assign(&mut self, rhs: UnsignedDuration) { + *self = *self - rhs + } +} + #[cfg(feature = "serde")] impl serde::Serialize for DateTime { #[inline] @@ -2555,6 +2598,109 @@ impl Iterator for DateTimeSeries { } } +/// Options for [`DateTime::checked_add`] and [`DateTime::checked_sub`]. +/// +/// This type provides a way to ergonomically add one of a few different +/// duration types to a [`DateTime`]. +/// +/// The main way to construct values of this type is with its `From` trait +/// implementations: +/// +/// * `From for DateTimeArithmetic` adds (or subtracts) the given span to +/// the receiver datetime. +/// * `From for DateTimeArithmetic` adds (or subtracts) +/// the given signed duration to the receiver datetime. +/// * `From for DateTimeArithmetic` adds (or subtracts) +/// the given unsigned duration to the receiver datetime. +/// +/// # Example +/// +/// ``` +/// use std::time::Duration; +/// +/// use jiff::{civil::date, SignedDuration, ToSpan}; +/// +/// let dt = date(2024, 2, 29).at(0, 0, 0, 0); +/// assert_eq!( +/// dt.checked_add(1.year())?, +/// date(2025, 2, 28).at(0, 0, 0, 0), +/// ); +/// assert_eq!( +/// dt.checked_add(SignedDuration::from_hours(24))?, +/// date(2024, 3, 1).at(0, 0, 0, 0), +/// ); +/// assert_eq!( +/// dt.checked_add(Duration::from_secs(24 * 60 * 60))?, +/// date(2024, 3, 1).at(0, 0, 0, 0), +/// ); +/// +/// # Ok::<(), Box>(()) +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct DateTimeArithmetic { + duration: Duration, +} + +impl DateTimeArithmetic { + #[inline] + fn checked_add(self, dt: DateTime) -> Result { + match self.duration.to_signed()? { + SDuration::Span(span) => dt.checked_add_span(span), + SDuration::Absolute(sdur) => dt.checked_add_duration(sdur), + } + } + + #[inline] + fn checked_neg(self) -> Result { + let duration = self.duration.checked_neg()?; + Ok(DateTimeArithmetic { duration }) + } + + #[inline] + fn is_negative(&self) -> bool { + self.duration.is_negative() + } +} + +impl From for DateTimeArithmetic { + fn from(span: Span) -> DateTimeArithmetic { + let duration = Duration::from(span); + DateTimeArithmetic { duration } + } +} + +impl From for DateTimeArithmetic { + fn from(sdur: SignedDuration) -> DateTimeArithmetic { + let duration = Duration::from(sdur); + DateTimeArithmetic { duration } + } +} + +impl From for DateTimeArithmetic { + fn from(udur: UnsignedDuration) -> DateTimeArithmetic { + let duration = Duration::from(udur); + DateTimeArithmetic { duration } + } +} + +impl<'a> From<&'a Span> for DateTimeArithmetic { + fn from(span: &'a Span) -> DateTimeArithmetic { + DateTimeArithmetic::from(*span) + } +} + +impl<'a> From<&'a SignedDuration> for DateTimeArithmetic { + fn from(sdur: &'a SignedDuration) -> DateTimeArithmetic { + DateTimeArithmetic::from(*sdur) + } +} + +impl<'a> From<&'a UnsignedDuration> for DateTimeArithmetic { + fn from(udur: &'a UnsignedDuration) -> DateTimeArithmetic { + DateTimeArithmetic::from(*udur) + } +} + /// Options for [`DateTime::since`] and [`DateTime::until`]. /// /// This type provides a way to configure the calculation of diff --git a/src/civil/mod.rs b/src/civil/mod.rs index 1b82d8af..468793d0 100644 --- a/src/civil/mod.rs +++ b/src/civil/mod.rs @@ -98,13 +98,15 @@ support time zones. */ pub use self::{ - date::{Date, DateDifference, DateSeries, DateWith}, + date::{Date, DateArithmetic, DateDifference, DateSeries, DateWith}, datetime::{ - DateTime, DateTimeDifference, DateTimeRound, DateTimeSeries, - DateTimeWith, + DateTime, DateTimeArithmetic, DateTimeDifference, DateTimeRound, + DateTimeSeries, DateTimeWith, }, iso_week_date::ISOWeekDate, - time::{Time, TimeDifference, TimeRound, TimeSeries, TimeWith}, + time::{ + Time, TimeArithmetic, TimeDifference, TimeRound, TimeSeries, TimeWith, + }, weekday::{Weekday, WeekdaysForward, WeekdaysReverse}, }; diff --git a/src/civil/time.rs b/src/civil/time.rs index 37c512d3..95d7b6f3 100644 --- a/src/civil/time.rs +++ b/src/civil/time.rs @@ -1,19 +1,22 @@ +use core::time::Duration as UnsignedDuration; + use crate::{ civil::{Date, DateTime}, - error::{err, Error}, + duration::{Duration, SDuration}, + error::{err, Error, ErrorContext}, fmt::{ self, temporal::{DEFAULT_DATETIME_PARSER, DEFAULT_DATETIME_PRINTER}, }, util::{ - rangeint::{RFrom, RInto}, + rangeint::{RFrom, RInto, TryRFrom}, round::increment, t::{ self, CivilDayNanosecond, Hour, Microsecond, Millisecond, Minute, Nanosecond, Second, SubsecNanosecond, C, }, }, - RoundMode, Span, SpanRound, Unit, Zoned, + RoundMode, SignedDuration, Span, SpanRound, Unit, Zoned, }; /// A representation of civil "wall clock" time. @@ -74,10 +77,10 @@ use crate::{ /// of `60`, then it is automatically constrained to `59`: /// /// ``` -/// use jiff::civil::Time; +/// use jiff::civil::{Time, time}; /// /// let t: Time = "23:59:60".parse()?; -/// assert_eq!(t, Time::constant(23, 59, 59, 0)); +/// assert_eq!(t, time(23, 59, 59, 0)); /// /// # Ok::<(), Box>(()) /// ``` @@ -89,10 +92,10 @@ use crate::{ /// then `t1 < t2`. For example: /// /// ``` -/// use jiff::civil::Time; +/// use jiff::civil::time; /// -/// let t1 = Time::constant(7, 30, 1, 0); -/// let t2 = Time::constant(8, 10, 0, 0); +/// let t1 = time(7, 30, 1, 0); +/// let t2 = time(8, 10, 0, 0); /// assert!(t1 < t2); /// ``` /// @@ -117,16 +120,16 @@ use crate::{ /// trait implementations: /// /// ``` -/// use jiff::{civil::Time, ToSpan}; +/// use jiff::{civil::time, ToSpan}; /// -/// let time = Time::constant(20, 10, 1, 0); +/// let t = time(20, 10, 1, 0); /// let span = 1.hours().minutes(49).seconds(59); -/// assert_eq!(time + span, Time::constant(22, 0, 0, 0)); +/// assert_eq!(t + span, time(22, 0, 0, 0)); /// /// // Overflow will result in wrap-around unless using checked /// // arithmetic explicitly. -/// let time = Time::constant(23, 59, 59, 999_999_999); -/// assert_eq!(Time::constant(0, 0, 0, 0), time + 1.nanoseconds()); +/// let t = time(23, 59, 59, 999_999_999); +/// assert_eq!(time(0, 0, 0, 0), t + 1.nanoseconds()); /// ``` /// /// Wrapping arithmetic is used by default because it corresponds to how clocks @@ -137,10 +140,10 @@ use crate::{ /// `Time` values directly via a `Sub` trait implementation: /// /// ``` -/// use jiff::{civil::Time, ToSpan}; +/// use jiff::{civil::time, ToSpan}; /// -/// let time1 = Time::constant(22, 0, 0, 0); -/// let time2 = Time::constant(20, 10, 1, 0); +/// let time1 = time(22, 0, 0, 0); +/// let time2 = time(20, 10, 1, 0); /// assert_eq!(time1 - time2, 1.hours().minutes(49).seconds(59)); /// ``` /// @@ -149,10 +152,10 @@ use crate::{ /// (as exemplified above), but we can ask for smaller units: /// /// ``` -/// use jiff::{civil::Time, ToSpan, Unit}; +/// use jiff::{civil::time, ToSpan, Unit}; /// -/// let time1 = Time::constant(23, 30, 0, 0); -/// let time2 = Time::constant(7, 0, 0, 0); +/// let time1 = time(23, 30, 0, 0); +/// let time2 = time(7, 0, 0, 0); /// assert_eq!( /// time1.since((Unit::Minute, time2))?, /// 990.minutes(), @@ -164,10 +167,10 @@ use crate::{ /// Or even round the span returned: /// /// ``` -/// use jiff::{civil::{Time, TimeDifference}, RoundMode, ToSpan, Unit}; +/// use jiff::{civil::{TimeDifference, time}, RoundMode, ToSpan, Unit}; /// -/// let time1 = Time::constant(23, 30, 0, 0); -/// let time2 = Time::constant(23, 35, 59, 0); +/// let time1 = time(23, 30, 0, 0); +/// let time2 = time(23, 35, 59, 0); /// assert_eq!( /// time1.until( /// TimeDifference::new(time2).smallest(Unit::Minute), @@ -196,16 +199,16 @@ use crate::{ /// to round to the nearest third hour: /// /// ``` -/// use jiff::{civil::{Time, TimeRound}, Unit}; +/// use jiff::{civil::{TimeRound, time}, Unit}; /// -/// let t = Time::constant(16, 27, 29, 999_999_999); +/// let t = time(16, 27, 29, 999_999_999); /// assert_eq!( /// t.round(TimeRound::new().smallest(Unit::Hour).increment(3))?, -/// Time::constant(15, 0, 0, 0), +/// time(15, 0, 0, 0), /// ); /// // Or alternatively, make use of the `From<(Unit, i64)> for TimeRound` /// // trait implementation: -/// assert_eq!(t.round((Unit::Hour, 3))?, Time::constant(15, 0, 0, 0)); +/// assert_eq!(t.round((Unit::Hour, 3))?, time(15, 0, 0, 0)); /// /// # Ok::<(), Box>(()) /// ``` @@ -402,9 +405,9 @@ impl Time { /// # Example /// /// ``` - /// use jiff::civil::Time; + /// use jiff::civil::time; /// - /// let t = Time::constant(13, 35, 56, 123_456_789); + /// let t = time(13, 35, 56, 123_456_789); /// assert_eq!(t.hour(), 13); /// ``` #[inline] @@ -417,9 +420,9 @@ impl Time { /// # Example /// /// ``` - /// use jiff::civil::Time; + /// use jiff::civil::time; /// - /// let t = Time::constant(13, 35, 56, 123_456_789); + /// let t = time(13, 35, 56, 123_456_789); /// assert_eq!(t.minute(), 35); /// ``` #[inline] @@ -432,9 +435,9 @@ impl Time { /// # Example /// /// ``` - /// use jiff::civil::Time; + /// use jiff::civil::time; /// - /// let t = Time::constant(13, 35, 56, 123_456_789); + /// let t = time(13, 35, 56, 123_456_789); /// assert_eq!(t.second(), 56); /// ``` #[inline] @@ -447,9 +450,9 @@ impl Time { /// # Example /// /// ``` - /// use jiff::civil::Time; + /// use jiff::civil::time; /// - /// let t = Time::constant(13, 35, 56, 123_456_789); + /// let t = time(13, 35, 56, 123_456_789); /// assert_eq!(t.millisecond(), 123); /// ``` #[inline] @@ -462,9 +465,9 @@ impl Time { /// # Example /// /// ``` - /// use jiff::civil::Time; + /// use jiff::civil::time; /// - /// let t = Time::constant(13, 35, 56, 123_456_789); + /// let t = time(13, 35, 56, 123_456_789); /// assert_eq!(t.microsecond(), 456); /// ``` #[inline] @@ -477,9 +480,9 @@ impl Time { /// # Example /// /// ``` - /// use jiff::civil::Time; + /// use jiff::civil::time; /// - /// let t = Time::constant(13, 35, 56, 123_456_789); + /// let t = time(13, 35, 56, 123_456_789); /// assert_eq!(t.nanosecond(), 789); /// ``` #[inline] @@ -587,6 +590,10 @@ impl Time { /// Add the given span to this time and wrap around on overflow. /// + /// This operation accepts three different duration types: [`Span`], + /// [`SignedDuration`] or [`std::time::Duration`]. This is achieved via + /// `From` trait implementations for the [`TimeArithmetic`] type. + /// /// # Properties /// /// Given times `t1` and `t2`, and a span `s`, with `t2 = t1 + s`, it @@ -601,23 +608,23 @@ impl Time { /// This routine can be used via the `+` operator. /// /// ``` - /// use jiff::{civil::Time, ToSpan}; + /// use jiff::{civil::time, ToSpan}; /// - /// let t = Time::constant(20, 10, 1, 0); + /// let t = time(20, 10, 1, 0); /// assert_eq!( /// t + 1.hours().minutes(49).seconds(59), - /// Time::constant(22, 0, 0, 0), + /// time(22, 0, 0, 0), /// ); /// ``` /// /// # Example: add nanoseconds to a `Time` /// /// ``` - /// use jiff::{civil::Time, ToSpan}; + /// use jiff::{civil::time, ToSpan}; /// - /// let t = Time::constant(22, 35, 1, 0); + /// let t = time(22, 35, 1, 0); /// assert_eq!( - /// Time::constant(22, 35, 3, 500_000_000), + /// time(22, 35, 3, 500_000_000), /// t.wrapping_add(2_500_000_000i64.nanoseconds()), /// ); /// ``` @@ -625,11 +632,11 @@ impl Time { /// # Example: add span with multiple units /// /// ``` - /// use jiff::{civil::Time, ToSpan}; + /// use jiff::{civil::time, ToSpan}; /// - /// let t = Time::constant(20, 10, 1, 0); + /// let t = time(20, 10, 1, 0); /// assert_eq!( - /// Time::constant(22, 0, 0, 0), + /// time(22, 0, 0, 0), /// t.wrapping_add(1.hours().minutes(49).seconds(59)), /// ); /// ``` @@ -637,19 +644,30 @@ impl Time { /// # Example: adding an empty span is a no-op /// /// ``` - /// use jiff::{civil::Time, Span}; + /// use jiff::{civil::time, Span}; /// - /// let t = Time::constant(20, 10, 1, 0); + /// let t = time(20, 10, 1, 0); /// assert_eq!(t, t.wrapping_add(Span::new())); /// ``` /// /// # Example: addition wraps on overflow /// /// ``` - /// use jiff::{civil::Time, ToSpan}; + /// use jiff::{civil::time, SignedDuration, ToSpan}; /// - /// let t = Time::constant(23, 59, 59, 999_999_999); - /// assert_eq!(Time::constant(0, 0, 0, 0), t.wrapping_add(1.nanoseconds())); + /// let t = time(23, 59, 59, 999_999_999); + /// assert_eq!( + /// t.wrapping_add(1.nanoseconds()), + /// time(0, 0, 0, 0), + /// ); + /// assert_eq!( + /// t.wrapping_add(SignedDuration::from_nanos(1)), + /// time(0, 0, 0, 0), + /// ); + /// assert_eq!( + /// t.wrapping_add(std::time::Duration::from_nanos(1)), + /// time(0, 0, 0, 0), + /// ); /// ``` /// /// Similarly, if there are any non-zero units greater than hours in the @@ -657,14 +675,20 @@ impl Time { /// ignored): /// /// ``` - /// use jiff::{civil::Time, ToSpan}; + /// use jiff::{civil::time, ToSpan}; /// /// // doesn't matter what our time value is in this example - /// let t = Time::constant(0, 0, 0, 0); + /// let t = time(0, 0, 0, 0); /// assert_eq!(t, t.wrapping_add(1.days())); /// ``` #[inline] - pub fn wrapping_add(self, span: Span) -> Time { + pub fn wrapping_add>(self, duration: A) -> Time { + let duration: TimeArithmetic = duration.into(); + duration.wrapping_add(self) + } + + #[inline] + fn wrapping_add_span(self, span: Span) -> Time { let mut sum = self.to_nanosecond().without_bounds(); sum = sum.wrapping_add( span.get_hours_ranged() @@ -696,95 +720,89 @@ impl Time { Time::from_nanosecond(civil_day_nanosecond) } - /// Subtract the given span from this time and wrap around on overflow. - /// - /// # Properties - /// - /// Given times `t1` and `t2`, and a span `s`, with `t2 = t1 - s`, it - /// follows then that `t1 = t2 + s` for all values of `t1` and `s` whose - /// difference is `t2`. - /// - /// In short, adding the given span from the difference returned by this - /// function is guaranteed to result in precisely the original time. - /// - /// # Example: available via subtraction operator + #[inline] + fn wrapping_add_signed_duration(self, duration: SignedDuration) -> Time { + let start = t::NoUnits128::rfrom(self.to_nanosecond()); + let duration = t::NoUnits128::new_unchecked(duration.as_nanos()); + let end = start.wrapping_add(duration) % t::NANOS_PER_CIVIL_DAY; + Time::from_nanosecond(end) + } + + #[inline] + fn wrapping_add_unsigned_duration( + self, + duration: UnsignedDuration, + ) -> Time { + let start = t::NoUnits128::rfrom(self.to_nanosecond()); + // OK because 96-bit unsigned integer can't overflow i128. + let duration = i128::try_from(duration.as_nanos()).unwrap(); + let duration = t::NoUnits128::new_unchecked(duration); + let duration = duration % t::NANOS_PER_CIVIL_DAY; + let end = start.wrapping_add(duration) % t::NANOS_PER_CIVIL_DAY; + Time::from_nanosecond(end) + } + + /// This routine is identical to [`Time::wrapping_add`] with the duration + /// negated. /// - /// This routine can be used via the `-` operator. + /// # Example /// /// ``` - /// use jiff::{civil::Time, ToSpan}; + /// use jiff::{civil::time, SignedDuration, ToSpan}; /// - /// let t = Time::constant(22, 0, 0, 0); + /// let t = time(0, 0, 0, 0); /// assert_eq!( - /// t - 1.hours().minutes(49).seconds(59), - /// Time::constant(20, 10, 1, 0), + /// t.wrapping_sub(1.nanoseconds()), + /// time(23, 59, 59, 999_999_999), /// ); - /// ``` - /// - /// # Example: subtract nanoseconds from a `Time` - /// - /// ``` - /// use jiff::{civil::Time, ToSpan}; - /// - /// let t = Time::constant(22, 35, 1, 0); /// assert_eq!( - /// Time::constant(22, 34, 58, 500_000_000), - /// t.wrapping_sub(2_500_000_000i64.nanoseconds()), + /// t.wrapping_sub(SignedDuration::from_nanos(1)), + /// time(23, 59, 59, 999_999_999), /// ); - /// ``` - /// - /// # Example: subtract span with multiple units - /// - /// ``` - /// use jiff::{civil::Time, ToSpan}; - /// - /// let t = Time::constant(22, 0, 0, 0); /// assert_eq!( - /// Time::constant(20, 10, 1, 0), - /// t.wrapping_sub(1.hours().minutes(49).seconds(59)), + /// t.wrapping_sub(std::time::Duration::from_nanos(1)), + /// time(23, 59, 59, 999_999_999), /// ); - /// ``` - /// - /// # Example: addition wraps on overflow - /// - /// ``` - /// use jiff::{civil::Time, Span}; - /// - /// let t = Time::constant(20, 10, 1, 0); - /// assert_eq!(t, t.wrapping_sub(Span::new())); - /// ``` /// - /// # Example: subtraction wraps on overflow - /// - /// ``` - /// use jiff::{civil::Time, ToSpan}; - /// - /// let t = Time::constant(0, 0, 0, 0); /// assert_eq!( - /// Time::constant(23, 59, 59, 999_999_999), - /// t.wrapping_sub(1.nanoseconds()), + /// t.wrapping_sub(SignedDuration::MIN), + /// time(15, 30, 8, 999_999_999), + /// ); + /// assert_eq!( + /// t.wrapping_sub(SignedDuration::MAX), + /// time(8, 29, 52, 1), + /// ); + /// assert_eq!( + /// t.wrapping_sub(std::time::Duration::MAX), + /// time(16, 59, 44, 1), /// ); - /// ``` - /// - /// Similarly, if there are any non-zero units greater than hours in the - /// given span, then they also result in wrapping behavior (i.e., they are - /// ignored): - /// - /// ``` - /// use jiff::{civil::Time, ToSpan}; - /// - /// // doesn't matter what our time value is in this example - /// let t = Time::constant(23, 59, 59, 999_999_999); - /// assert_eq!(t, t.wrapping_sub(1.days())); /// ``` #[inline] - pub fn wrapping_sub(self, span: Span) -> Time { - self.wrapping_add(span.negate()) + pub fn wrapping_sub>(self, duration: A) -> Time { + let duration: TimeArithmetic = duration.into(); + duration.wrapping_sub(self) + } + + #[inline] + fn wrapping_sub_unsigned_duration( + self, + duration: UnsignedDuration, + ) -> Time { + let start = t::NoUnits128::rfrom(self.to_nanosecond()); + // OK because 96-bit unsigned integer can't overflow i128. + let duration = i128::try_from(duration.as_nanos()).unwrap(); + let duration = t::NoUnits128::new_unchecked(duration); + let end = start.wrapping_sub(duration) % t::NANOS_PER_CIVIL_DAY; + Time::from_nanosecond(end) } /// Add the given span to this time and return an error if the result would /// otherwise overflow. /// + /// This operation accepts three different duration types: [`Span`], + /// [`SignedDuration`] or [`std::time::Duration`]. This is achieved via + /// `From` trait implementations for the [`TimeArithmetic`] type. + /// /// # Properties /// /// Given a time `t1` and a span `s`, and assuming `t2 = t1 + s` exists, it @@ -805,11 +823,11 @@ impl Time { /// # Example: add nanoseconds to a `Time` /// /// ``` - /// use jiff::{civil::Time, ToSpan}; + /// use jiff::{civil::time, ToSpan}; /// - /// let t = Time::constant(22, 35, 1, 0); + /// let t = time(22, 35, 1, 0); /// assert_eq!( - /// Time::constant(22, 35, 3, 500_000_000), + /// time(22, 35, 3, 500_000_000), /// t.checked_add(2_500_000_000i64.nanoseconds())?, /// ); /// # Ok::<(), Box>(()) @@ -818,11 +836,11 @@ impl Time { /// # Example: add span with multiple units /// /// ``` - /// use jiff::{civil::Time, ToSpan}; + /// use jiff::{civil::time, ToSpan}; /// - /// let t = Time::constant(20, 10, 1, 0); + /// let t = time(20, 10, 1, 0); /// assert_eq!( - /// Time::constant(22, 0, 0, 0), + /// time(22, 0, 0, 0), /// t.checked_add(1.hours().minutes(49).seconds(59))?, /// ); /// # Ok::<(), Box>(()) @@ -831,9 +849,9 @@ impl Time { /// # Example: adding an empty span is a no-op /// /// ``` - /// use jiff::{civil::Time, Span}; + /// use jiff::{civil::time, Span}; /// - /// let t = Time::constant(20, 10, 1, 0); + /// let t = time(20, 10, 1, 0); /// assert_eq!(t, t.checked_add(Span::new())?); /// /// # Ok::<(), Box>(()) @@ -862,14 +880,46 @@ impl Time { /// given span, then they also result in overflow (and thus an error): /// /// ``` - /// use jiff::{civil::Time, ToSpan}; + /// use jiff::{civil::time, ToSpan}; /// /// // doesn't matter what our time value is in this example - /// let t = Time::constant(0, 0, 0, 0); + /// let t = time(0, 0, 0, 0); /// assert!(t.checked_add(1.days()).is_err()); /// ``` + /// + /// # Example: adding absolute durations + /// + /// This shows how to add signed and unsigned absolute durations to a + /// `Time`. As with adding a `Span`, any overflow that occurs results in + /// an error. + /// + /// ``` + /// use std::time::Duration; + /// + /// use jiff::{civil::time, SignedDuration}; + /// + /// let t = time(23, 0, 0, 0); + /// + /// let dur = SignedDuration::from_mins(30); + /// assert_eq!(t.checked_add(dur)?, time(23, 30, 0, 0)); + /// assert_eq!(t.checked_add(-dur)?, time(22, 30, 0, 0)); + /// + /// let dur = Duration::new(0, 1); + /// assert_eq!(t.checked_add(dur)?, time(23, 0, 0, 1)); + /// + /// # Ok::<(), Box>(()) + /// ``` #[inline] - pub fn checked_add(self, span: Span) -> Result { + pub fn checked_add>( + self, + duration: A, + ) -> Result { + let duration: TimeArithmetic = duration.into(); + duration.checked_add(self) + } + + #[inline] + fn checked_add_span(self, span: Span) -> Result { let (time, span) = self.overflowing_add(span)?; if let Some(err) = span.smallest_non_time_non_zero_unit_error() { return Err(err); @@ -877,139 +927,77 @@ impl Time { Ok(time) } - /// Subtract the given span from this time and return an error if the - /// result would otherwise overflow. - /// - /// # Properties - /// - /// Given a time `t1` and a span `s`, and assuming `t2 = t1 - s` exists, it - /// follows then that `t1 = t2 + s` for all values of `t1` and `s` whose - /// difference is a valid `t2`. - /// - /// In short, adding the given span from the difference returned by this - /// function is guaranteed to result in precisely the original time. + #[inline] + fn checked_add_duration( + self, + duration: SignedDuration, + ) -> Result { + let original = duration; + let start = t::NoUnits128::rfrom(self.to_nanosecond()); + let duration = t::NoUnits128::new_unchecked(duration.as_nanos()); + // This can never fail because the maximum duration fits into a + // 96-bit integer, and adding any 96-bit integer to any 64-bit + // integer can never overflow a 128-bit integer. + let end = start.try_checked_add("nanoseconds", duration).unwrap(); + let end = CivilDayNanosecond::try_rfrom("nanoseconds", end) + .with_context(|| { + err!( + "adding signed duration {duration:?}, equal to + {nanos} nanoseconds, to {time} overflowed", + duration = original, + nanos = original.as_nanos(), + time = self, + ) + })?; + Ok(Time::from_nanosecond(end)) + } + + /// This routine is identical to [`Time::checked_add`] with the duration + /// negated. /// /// # Errors /// - /// This returns an error in two cases: + /// This has the same error conditions as [`Time::checked_add`]. /// - /// 1. When the given span's interval overflows the maximum allowed - /// duration. - /// 2. When subtracting the span to a time value would exceed the minimum - /// allowed value (`00:00:00.000000000`). This also automatically happens - /// whenever the given span has any non-zero values for units bigger than - /// hours. - /// - /// # Example: subtract nanoseconds to a `Time` - /// - /// ``` - /// use jiff::{civil::Time, ToSpan}; + /// # Example /// - /// let t = Time::constant(22, 35, 1, 0); - /// assert_eq!( - /// Time::constant(22, 34, 58, 500_000_000), - /// t.checked_sub(2_500_000_000i64.nanoseconds())?, - /// ); - /// # Ok::<(), Box>(()) /// ``` + /// use std::time::Duration; /// - /// # Example: subtract span with multiple units + /// use jiff::{civil::time, SignedDuration, ToSpan}; /// - /// ``` - /// use jiff::{civil::Time, ToSpan}; - /// - /// let t = Time::constant(22, 0, 0, 0); + /// let t = time(22, 0, 0, 0); /// assert_eq!( - /// Time::constant(20, 10, 1, 0), /// t.checked_sub(1.hours().minutes(49).seconds(59))?, + /// time(20, 10, 1, 0), /// ); - /// # Ok::<(), Box>(()) - /// ``` - /// - /// # Example: subtracting an empty span is a no-op - /// - /// ``` - /// use jiff::{civil::Time, Span}; - /// - /// let t = Time::constant(20, 10, 1, 0); - /// assert_eq!(t, t.checked_sub(Span::new())?); - /// # Ok::<(), Box>(()) - /// ``` - /// - /// # Example: error on overflow - /// - /// ``` - /// use jiff::{civil::time, ToSpan}; - /// - /// // okay - /// let t = time(0, 0, 0, 1); /// assert_eq!( - /// t.with().nanosecond(0).build()?, - /// t.checked_sub(1.nanoseconds())?, + /// t.checked_sub(SignedDuration::from_hours(1))?, + /// time(21, 0, 0, 0), + /// ); + /// assert_eq!( + /// t.checked_sub(Duration::from_secs(60 * 60))?, + /// time(21, 0, 0, 0), /// ); - /// - /// // not okay - /// let t = time(0, 0, 0, 0); - /// assert!(t.checked_sub(1.nanoseconds()).is_err()); - /// /// # Ok::<(), Box>(()) /// ``` - /// - /// Similarly, if there are any non-zero units greater than hours in the - /// given span, then they also result in overflow (and thus an error): - /// - /// ``` - /// use jiff::{civil::Time, ToSpan}; - /// - /// // doesn't matter what our time value is in this example - /// let t = Time::constant(23, 59, 59, 999_999_999); - /// assert!(t.checked_sub(1.days()).is_err()); - /// ``` #[inline] - pub fn checked_sub(self, span: Span) -> Result { - self.checked_add(span.negate()) + pub fn checked_sub>( + self, + duration: A, + ) -> Result { + let duration: TimeArithmetic = duration.into(); + duration.checked_neg().and_then(|ta| ta.checked_add(self)) } - /// Add the given span to this time and saturate if the addition would - /// otherwise overflow. - /// - /// # Example: add nanoseconds to a `Time` - /// - /// ``` - /// use jiff::{civil::Time, ToSpan}; - /// - /// let t = Time::constant(22, 35, 1, 0); - /// assert_eq!( - /// Time::constant(22, 35, 3, 500_000_000), - /// t.saturating_add(2_500_000_000i64.nanoseconds()), - /// ); - /// ``` - /// - /// # Example: add span with multiple units - /// - /// ``` - /// use jiff::{civil::Time, ToSpan}; - /// - /// let t = Time::constant(20, 10, 1, 0); - /// assert_eq!( - /// Time::constant(22, 0, 0, 0), - /// t.saturating_add(1.hours().minutes(49).seconds(59)), - /// ); - /// ``` - /// - /// # Example: adding an empty span is a no-op + /// This routine is identical to [`Time::checked_add`], except the + /// result saturates on overflow. That is, instead of overflow, either + /// [`Time::MIN`] or [`Time::MAX`] is returned. /// - /// ``` - /// use jiff::{civil::Time, Span}; + /// # Example /// - /// let t = Time::constant(20, 10, 1, 0); - /// assert_eq!(t, t.saturating_add(Span::new())); /// ``` - /// - /// # Example: saturate on overflow - /// - /// ``` - /// use jiff::{civil::{Time, time}, ToSpan}; + /// use jiff::{civil::{Time, time}, SignedDuration, ToSpan}; /// /// // no saturation /// let t = time(23, 59, 59, 999_999_998); @@ -1021,6 +1009,9 @@ impl Time { /// // saturates /// let t = time(23, 59, 59, 999_999_999); /// assert_eq!(Time::MAX, t.saturating_add(1.nanoseconds())); + /// assert_eq!(Time::MAX, t.saturating_add(SignedDuration::MAX)); + /// assert_eq!(Time::MIN, t.saturating_add(SignedDuration::MIN)); + /// assert_eq!(Time::MAX, t.saturating_add(std::time::Duration::MAX)); /// /// # Ok::<(), Box>(()) /// ``` @@ -1036,9 +1027,10 @@ impl Time { /// assert_eq!(Time::MAX, t.saturating_add(1.days())); /// ``` #[inline] - pub fn saturating_add(self, span: Span) -> Time { - self.checked_add(span).unwrap_or_else(|_| { - if span.is_negative() { + pub fn saturating_add>(self, duration: A) -> Time { + let duration: TimeArithmetic = duration.into(); + self.checked_add(duration).unwrap_or_else(|_| { + if duration.is_negative() { Time::MIN } else { Time::MAX @@ -1046,46 +1038,13 @@ impl Time { }) } - /// Subtract the given span from this time and saturate if the subtraction - /// would otherwise overflow. - /// - /// # Example: subtract nanoseconds to a `Time` - /// - /// ``` - /// use jiff::{civil::Time, ToSpan}; - /// - /// let t = Time::constant(22, 35, 1, 0); - /// assert_eq!( - /// Time::constant(22, 34, 58, 500_000_000), - /// t.saturating_sub(2_500_000_000i64.nanoseconds()), - /// ); - /// ``` - /// - /// # Example: subtract span with multiple units - /// - /// ``` - /// use jiff::{civil::Time, ToSpan}; - /// - /// let t = Time::constant(22, 0, 0, 0); - /// assert_eq!( - /// Time::constant(20, 10, 1, 0), - /// t.saturating_sub(1.hours().minutes(49).seconds(59)), - /// ); - /// ``` + /// This routine is identical to [`Time::saturating_add`] with the duration + /// negated. /// - /// # Example: subtracting an empty span is a no-op - /// - /// ``` - /// use jiff::{civil::Time, Span}; - /// - /// let t = Time::constant(20, 10, 1, 0); - /// assert_eq!(t, t.saturating_sub(Span::new())); - /// ``` - /// - /// # Example: saturate on overflow + /// # Example /// /// ``` - /// use jiff::{civil::{Time, time}, ToSpan}; + /// use jiff::{civil::{Time, time}, SignedDuration, ToSpan}; /// /// // no saturation /// let t = time(0, 0, 0, 1); @@ -1097,23 +1056,17 @@ impl Time { /// // saturates /// let t = time(0, 0, 0, 0); /// assert_eq!(Time::MIN, t.saturating_sub(1.nanoseconds())); + /// assert_eq!(Time::MIN, t.saturating_sub(SignedDuration::MAX)); + /// assert_eq!(Time::MAX, t.saturating_sub(SignedDuration::MIN)); + /// assert_eq!(Time::MIN, t.saturating_sub(std::time::Duration::MAX)); /// /// # Ok::<(), Box>(()) /// ``` - /// - /// Similarly, if there are any non-zero units greater than hours in the - /// given span, then they also result in overflow (and thus saturation): - /// - /// ``` - /// use jiff::{civil::{Time, time}, ToSpan}; - /// - /// // doesn't matter what our time value is in this example - /// let t = time(23, 59, 59, 999_999_999); - /// assert_eq!(Time::MIN, t.saturating_sub(1.days())); - /// ``` #[inline] - pub fn saturating_sub(self, span: Span) -> Time { - self.saturating_add(span.negate()) + pub fn saturating_sub>(self, duration: A) -> Time { + let duration: TimeArithmetic = duration.into(); + let Ok(duration) = duration.checked_neg() else { return Time::MIN }; + self.saturating_add(duration) } /// Adds the given span to the this time value, and returns the resulting @@ -1146,10 +1099,36 @@ impl Time { Ok((time, Span::new().days_ranged(days))) } - /// Returns a span representing the elapsed time from this time since + /// Like `overflowing_add`, but with `SignedDuration`. + /// + /// This is used for datetime arithmetic, when adding to the time + /// component overflows into days (always 24 hours). + #[inline] + pub(crate) fn overflowing_add_duration( + self, + duration: SignedDuration, + ) -> Result<(Time, SignedDuration), Error> { + let start = t::NoUnits128::rfrom(self.to_nanosecond()); + let duration = t::NoUnits96::new_unchecked(duration.as_nanos()); + // This can never fail because the maximum duration fits into a + // 96-bit integer, and adding any 96-bit integer to any 64-bit + // integer can never overflow a 128-bit integer. + let sum = start.try_checked_add("nanoseconds", duration).unwrap(); + let days = t::SpanDays::try_new( + "overflowing-days", + sum.div_floor(t::NANOS_PER_CIVIL_DAY), + )?; + let time_nanos = sum.rem_floor(t::NANOS_PER_CIVIL_DAY); + let time = Time::from_nanosecond(time_nanos); + // OK because of the constraint imposed by t::SpanDays. + let hours = i64::from(days).checked_mul(24).unwrap(); + Ok((time, SignedDuration::from_hours(hours))) + } + + /// Returns a span representing the elapsed time from this time until /// the given `other` time. /// - /// When `other` is later than this time, the span returned will be + /// When `other` is earlier than this time, the span returned will be /// negative. /// /// Depending on the input provided, the span returned is rounded. It may @@ -1171,8 +1150,8 @@ impl Time { /// # Errors /// /// An error can occur if `TimeDifference` is misconfigured. For example, - /// if the smallest unit provided is bigger than the largest unit. Or if - /// a unit greater than `Unit::Hour` is provided. + /// if the smallest unit provided is bigger than the largest unit, or if + /// the largest unit is bigger than [`Unit::Hour`]. /// /// It is guaranteed that if one provides a time with the default /// [`TimeDifference`] configuration, then this routine will never fail. @@ -1180,31 +1159,17 @@ impl Time { /// # Examples /// /// ``` - /// use jiff::{civil::Time, ToSpan}; + /// use jiff::{civil::time, ToSpan}; /// - /// let t1 = Time::constant(22, 35, 1, 0); - /// let t2 = Time::constant(22, 35, 3, 500_000_000); - /// assert_eq!(t2.since(t1)?, 2.seconds().milliseconds(500)); + /// let t1 = time(22, 35, 1, 0); + /// let t2 = time(22, 35, 3, 500_000_000); + /// assert_eq!(t1.until(t2)?, 2.seconds().milliseconds(500)); /// // Flipping the dates is fine, but you'll get a negative span. - /// assert_eq!(t1.since(t2)?, -2.seconds().milliseconds(500)); + /// assert_eq!(t2.until(t1)?, -2.seconds().milliseconds(500)); /// /// # Ok::<(), Box>(()) /// ``` /// - /// # Example: available via subtraction operator - /// - /// This routine can be used via the `-` operator. Since the default - /// configuration is used and because a `Span` can represent the difference - /// between any two possible times, it will never panic. - /// - /// ``` - /// use jiff::{civil::Time, ToSpan}; - /// - /// let earlier = Time::constant(1, 0, 0, 0); - /// let later = Time::constant(22, 30, 0, 0); - /// assert_eq!(later - earlier, 21.hours().minutes(30)); - /// ``` - /// /// # Example: using smaller units /// /// This example shows how to contract the span returned to smaller units. @@ -1212,23 +1177,57 @@ impl Time { /// trait implementation. /// /// ``` - /// use jiff::{civil::Time, Unit, ToSpan}; + /// use jiff::{civil::time, Unit, ToSpan}; /// - /// let t1 = Time::constant(3, 24, 30, 3500); - /// let t2 = Time::constant(15, 30, 0, 0); + /// let t1 = time(3, 24, 30, 3500); + /// let t2 = time(15, 30, 0, 0); /// /// // The default limits spans to using "hours" as the biggest unit. - /// let span = t2.since(t1)?; + /// let span = t1.until(t2)?; /// assert_eq!(span.to_string(), "PT12h5m29.9999965s"); /// /// // But we can ask for smaller units, like capping the biggest unit /// // to minutes instead of hours. - /// let span = t2.since((Unit::Minute, t1))?; + /// let span = t1.until((Unit::Minute, t2))?; /// assert_eq!(span.to_string(), "PT725m29.9999965s"); /// /// # Ok::<(), Box>(()) /// ``` #[inline] + pub fn until>( + self, + other: A, + ) -> Result { + let args: TimeDifference = other.into(); + let span = args.until_with_largest_unit(self)?; + if args.rounding_may_change_span() { + span.round(args.round) + } else { + Ok(span) + } + } + + /// This routine is identical to [`Time::until`], but the order of the + /// parameters is flipped. + /// + /// # Errors + /// + /// This has the same error conditions as [`Time::until`]. + /// + /// # Example + /// + /// This routine can be used via the `-` operator. Since the default + /// configuration is used and because a `Span` can represent the difference + /// between any two possible times, it will never panic. + /// + /// ``` + /// use jiff::{civil::time, ToSpan}; + /// + /// let earlier = time(1, 0, 0, 0); + /// let later = time(22, 30, 0, 0); + /// assert_eq!(later - earlier, 21.hours().minutes(30)); + /// ``` + #[inline] pub fn since>( self, other: A, @@ -1242,77 +1241,127 @@ impl Time { } } - /// Returns a span representing the elapsed time from this time until - /// the given `other` time. + /// Returns an absolute duration representing the elapsed time from this + /// time until the given `other` time. /// - /// When `other` is earlier than this time, the span returned will be - /// negative. + /// When `other` occurs before this time, then the duration returned will + /// be negative. /// - /// Depending on the input provided, the span returned is rounded. It may - /// also be balanced down to smaller units than the default. By default, - /// the span returned is balanced such that the biggest possible unit is + /// Unlike [`Time::until`], this returns a duration corresponding to a + /// 96-bit integer of nanoseconds between two times. In this case of + /// computing durations between civil times where all days are assumed to + /// be 24 hours long, the duration returned will always be less than 24 /// hours. /// - /// This operation is configured by providing a [`TimeDifference`] - /// value. Since this routine accepts anything that implements - /// `Into`, once can pass a `Time` directly. One - /// can also pass a `(Unit, Time)`, where `Unit` is treated as - /// [`TimeDifference::largest`]. + /// # Fallibility /// - /// # Properties + /// This routine never panics or returns an error. Since there are no + /// configuration options that can be incorrectly provided, no error is + /// possible when calling this routine. In contrast, [`Time::until`] can + /// return an error in some cases due to misconfiguration. But like this + /// routine, [`Time::until`] never panics or returns an error in its + /// default configuration. /// - /// As long as no rounding is requested, it is guaranteed that adding the - /// span returned to the `other` time will always equal this time. + /// # When should I use this versus [`Time::until`]? /// - /// # Examples + /// See the type documentation for [`SignedDuration`] for the section on + /// when one should use [`Span`] and when one should use `SignedDuration`. + /// In short, use `Span` (and therefore `Time::until`) unless you have a + /// specific reason to do otherwise. + /// + /// # Example /// /// ``` - /// use jiff::{civil::Time, ToSpan}; + /// use jiff::{civil::time, SignedDuration}; /// - /// let t1 = Time::constant(22, 35, 1, 0); - /// let t2 = Time::constant(22, 35, 3, 500_000_000); - /// assert_eq!(t1.until(t2)?, 2.seconds().milliseconds(500)); - /// // Flipping the dates is fine, but you'll get a negative span. - /// assert_eq!(t2.until(t1)?, -2.seconds().milliseconds(500)); + /// let t1 = time(22, 35, 1, 0); + /// let t2 = time(22, 35, 3, 500_000_000); + /// assert_eq!(t1.duration_until(t2), SignedDuration::new(2, 500_000_000)); + /// // Flipping the time is fine, but you'll get a negative duration. + /// assert_eq!(t2.duration_until(t1), -SignedDuration::new(2, 500_000_000)); + /// ``` + /// + /// # Example: difference with [`Time::until`] + /// + /// Since the difference between two civil times is always expressed in + /// units of hours or smaller, and units of hours or smaller are always + /// uniform, there is no "expressive" difference between this routine and + /// `Time::until`. The only difference is that this routine returns a + /// `SignedDuration` and `Time::until` returns a [`Span`]. Moreover, since + /// the difference is always less than 24 hours, the return values can + /// always be infallibly and losslessly converted between each other: /// - /// # Ok::<(), Box>(()) /// ``` + /// use jiff::{civil::time, SignedDuration, Span}; /// - /// # Example: using smaller units + /// let t1 = time(22, 35, 1, 0); + /// let t2 = time(22, 35, 3, 500_000_000); + /// let dur = t1.duration_until(t2); + /// // Guaranteed to never fail because the duration + /// // between two civil times never exceeds the limits + /// // of a `Span`. + /// let span = Span::try_from(dur).unwrap(); + /// assert_eq!(span, Span::new().seconds(2).milliseconds(500)); + /// // Guaranteed to succeed and always return the original + /// // duration because the units are always hours or smaller, + /// // and thus uniform. This means a relative datetime is + /// // never required to do this conversion. + /// let dur = SignedDuration::try_from(span).unwrap(); + /// assert_eq!(dur, SignedDuration::new(2, 500_000_000)); + /// ``` /// - /// This example shows how to contract the span returned to smaller units. - /// This makes use of a `From<(Unit, Time)> for TimeDifference` - /// trait implementation. + /// This conversion guarantee also applies to [`Time::until`] since it + /// always returns a balanced span. That is, it never returns spans like + /// `1 second 1000 milliseconds`. (Those cannot be losslessly converted to + /// a `SignedDuration` since a `SignedDuration` is only represented as a + /// single 96-bit integer of nanoseconds.) + /// + /// # Example: getting an unsigned duration + /// + /// If you're looking to find the duration between two times as a + /// [`std::time::Duration`], you'll need to use this method to get a + /// [`SignedDuration`] and then convert it to a `std::time::Duration`: /// /// ``` - /// use jiff::{civil::Time, Unit, ToSpan}; + /// use std::time::Duration; /// - /// let t1 = Time::constant(3, 24, 30, 3500); - /// let t2 = Time::constant(15, 30, 0, 0); + /// use jiff::{civil::time, SignedDuration, Span}; /// - /// // The default limits spans to using "hours" as the biggest unit. - /// let span = t1.until(t2)?; - /// assert_eq!(span.to_string(), "PT12h5m29.9999965s"); + /// let t1 = time(22, 35, 1, 0); + /// let t2 = time(22, 35, 3, 500_000_000); + /// let dur = Duration::try_from(t1.duration_until(t2))?;; + /// assert_eq!(dur, Duration::new(2, 500_000_000)); /// - /// // But we can ask for smaller units, like capping the biggest unit - /// // to minutes instead of hours. - /// let span = t1.until((Unit::Minute, t2))?; - /// assert_eq!(span.to_string(), "PT725m29.9999965s"); + /// // Note that unsigned durations cannot represent all + /// // possible differences! If the duration would be negative, + /// // then the conversion fails: + /// assert!(Duration::try_from(t2.duration_until(t1)).is_err()); /// /// # Ok::<(), Box>(()) /// ``` #[inline] - pub fn until>( - self, - other: A, - ) -> Result { - let args: TimeDifference = other.into(); - let span = args.until_with_largest_unit(self)?; - if args.rounding_may_change_span() { - span.round(args.round) - } else { - Ok(span) - } + pub fn duration_until(self, other: Time) -> SignedDuration { + SignedDuration::time_until(self, other) + } + + /// This routine is identical to [`Time::duration_until`], but the order of + /// the parameters is flipped. + /// + /// # Example + /// + /// ``` + /// use jiff::{civil::time, SignedDuration}; + /// + /// let earlier = time(1, 0, 0, 0); + /// let later = time(22, 30, 0, 0); + /// assert_eq!( + /// later.duration_since(earlier), + /// SignedDuration::from_secs((21 * 60 * 60) + (30 * 60)), + /// ); + /// ``` + #[inline] + pub fn duration_since(self, other: Time) -> SignedDuration { + SignedDuration::time_until(other, self) } /// Rounds this time according to the [`TimeRound`] configuration given. @@ -1357,17 +1406,17 @@ impl Time { /// smallest unit directly, instead of constructing a `TimeRound` manually. /// /// ``` - /// use jiff::{civil::Time, Unit}; + /// use jiff::{civil::time, Unit}; /// - /// let t = Time::constant(15, 45, 10, 123_456_789); + /// let t = time(15, 45, 10, 123_456_789); /// assert_eq!( /// t.round(Unit::Second)?, - /// Time::constant(15, 45, 10, 0), + /// time(15, 45, 10, 0), /// ); - /// let t = Time::constant(15, 45, 10, 500_000_001); + /// let t = time(15, 45, 10, 500_000_001); /// assert_eq!( /// t.round(Unit::Second)?, - /// Time::constant(15, 45, 11, 0), + /// time(15, 45, 11, 0), /// ); /// /// # Ok::<(), Box>(()) @@ -1380,12 +1429,12 @@ impl Time { /// [`RoundMode::Trunc`] can be used too: /// /// ``` - /// use jiff::{civil::{Time, TimeRound}, RoundMode, Unit}; + /// use jiff::{civil::{TimeRound, time}, RoundMode, Unit}; /// - /// let t = Time::constant(15, 45, 10, 999_999_999); + /// let t = time(15, 45, 10, 999_999_999); /// assert_eq!( /// t.round(Unit::Second)?, - /// Time::constant(15, 45, 11, 0), + /// time(15, 45, 11, 0), /// ); /// // The default will round up to the next second for any fraction /// // greater than or equal to 0.5. But truncation will always round @@ -1394,7 +1443,7 @@ impl Time { /// t.round( /// TimeRound::new().smallest(Unit::Second).mode(RoundMode::Trunc), /// )?, - /// Time::constant(15, 45, 10, 0), + /// time(15, 45, 10, 0), /// ); /// /// # Ok::<(), Box>(()) @@ -1403,14 +1452,14 @@ impl Time { /// # Example: rounding to the nearest 5 minute increment /// /// ``` - /// use jiff::{civil::Time, Unit}; + /// use jiff::{civil::time, Unit}; /// /// // rounds down - /// let t = Time::constant(15, 27, 29, 999_999_999); - /// assert_eq!(t.round((Unit::Minute, 5))?, Time::constant(15, 25, 0, 0)); + /// let t = time(15, 27, 29, 999_999_999); + /// assert_eq!(t.round((Unit::Minute, 5))?, time(15, 25, 0, 0)); /// // rounds up - /// let t = Time::constant(15, 27, 30, 0); - /// assert_eq!(t.round((Unit::Minute, 5))?, Time::constant(15, 30, 0, 0)); + /// let t = time(15, 27, 30, 0); + /// assert_eq!(t.round((Unit::Minute, 5))?, time(15, 30, 0, 0)); /// /// # Ok::<(), Box>(()) /// ``` @@ -1429,7 +1478,7 @@ impl Time { /// # Ok::<(), Box>(()) /// ``` #[inline] - pub fn round(self, options: impl Into) -> Result { + pub fn round>(self, options: R) -> Result { let options: TimeRound = options.into(); options.round(self) } @@ -1446,37 +1495,37 @@ impl Time { /// This shows how to visit each third hour of a 24 hour time interval: /// /// ``` - /// use jiff::{civil::Time, ToSpan}; + /// use jiff::{civil::{Time, time}, ToSpan}; /// /// let start = Time::MIN; /// let mut every_third_hour = vec![]; - /// for time in start.series(3.hours()) { - /// every_third_hour.push(time); + /// for t in start.series(3.hours()) { + /// every_third_hour.push(t); /// } /// assert_eq!(every_third_hour, vec![ - /// Time::constant(0, 0, 0, 0), - /// Time::constant(3, 0, 0, 0), - /// Time::constant(6, 0, 0, 0), - /// Time::constant(9, 0, 0, 0), - /// Time::constant(12, 0, 0, 0), - /// Time::constant(15, 0, 0, 0), - /// Time::constant(18, 0, 0, 0), - /// Time::constant(21, 0, 0, 0), + /// time(0, 0, 0, 0), + /// time(3, 0, 0, 0), + /// time(6, 0, 0, 0), + /// time(9, 0, 0, 0), + /// time(12, 0, 0, 0), + /// time(15, 0, 0, 0), + /// time(18, 0, 0, 0), + /// time(21, 0, 0, 0), /// ]); /// ``` /// /// Or go backwards every 6.5 hours: /// /// ``` - /// use jiff::{civil::Time, ToSpan}; + /// use jiff::{civil::{Time, time}, ToSpan}; /// - /// let start = Time::constant(23, 0, 0, 0); + /// let start = time(23, 0, 0, 0); /// let times: Vec