jiff/
timestamp.rs

1use core::time::Duration as UnsignedDuration;
2
3use crate::{
4    duration::{Duration, SDuration},
5    error::{err, Error, ErrorContext},
6    fmt::{
7        self,
8        temporal::{self, DEFAULT_DATETIME_PARSER},
9    },
10    tz::{Offset, TimeZone},
11    util::{
12        rangeint::{RFrom, RInto},
13        round::increment,
14        t::{
15            self, FractionalNanosecond, NoUnits, NoUnits128, UnixMicroseconds,
16            UnixMilliseconds, UnixNanoseconds, UnixSeconds, C,
17        },
18    },
19    zoned::Zoned,
20    RoundMode, SignedDuration, Span, SpanRound, Unit,
21};
22
23/// An instant in time represented as the number of nanoseconds since the Unix
24/// epoch.
25///
26/// A timestamp is always in the Unix timescale with a UTC offset of zero.
27///
28/// To obtain civil or "local" datetime units like year, month, day or hour, a
29/// timestamp needs to be combined with a [`TimeZone`] to create a [`Zoned`].
30/// That can be done with [`Timestamp::in_tz`] or [`Timestamp::to_zoned`].
31///
32/// The integer count of nanoseconds since the Unix epoch is signed, where
33/// the Unix epoch is `1970-01-01 00:00:00Z`. A positive timestamp indicates
34/// a point in time after the Unix epoch. A negative timestamp indicates a
35/// point in time before the Unix epoch.
36///
37/// # Parsing and printing
38///
39/// The `Timestamp` type provides convenient trait implementations of
40/// [`std::str::FromStr`] and [`std::fmt::Display`]:
41///
42/// ```
43/// use jiff::Timestamp;
44///
45/// let ts: Timestamp = "2024-06-19 15:22:45-04".parse()?;
46/// assert_eq!(ts.to_string(), "2024-06-19T19:22:45Z");
47///
48/// # Ok::<(), Box<dyn std::error::Error>>(())
49/// ```
50///
51/// A `Timestamp` can also be parsed from something that _contains_ a
52/// timestamp, but with perhaps other data (such as a time zone):
53///
54/// ```
55/// use jiff::Timestamp;
56///
57/// let ts: Timestamp = "2024-06-19T15:22:45-04[America/New_York]".parse()?;
58/// assert_eq!(ts.to_string(), "2024-06-19T19:22:45Z");
59///
60/// # Ok::<(), Box<dyn std::error::Error>>(())
61/// ```
62///
63/// For more information on the specific format supported, see the
64/// [`fmt::temporal`](crate::fmt::temporal) module documentation.
65///
66/// # Default value
67///
68/// For convenience, this type implements the `Default` trait. Its default
69/// value corresponds to `1970-01-01T00:00:00.000000000`. That is, it is the
70/// Unix epoch. One can also access this value via the `Timestamp::UNIX_EPOCH`
71/// constant.
72///
73/// # Leap seconds
74///
75/// Jiff does not support leap seconds. Jiff behaves as if they don't exist.
76/// The only exception is that if one parses a timestamp with a second
77/// component of `60`, then it is automatically constrained to `59`:
78///
79/// ```
80/// use jiff::Timestamp;
81///
82/// let ts: Timestamp = "2016-12-31 23:59:60Z".parse()?;
83/// assert_eq!(ts.to_string(), "2016-12-31T23:59:59Z");
84///
85/// # Ok::<(), Box<dyn std::error::Error>>(())
86/// ```
87///
88/// # Comparisons
89///
90/// The `Timestamp` type provides both `Eq` and `Ord` trait implementations
91/// to facilitate easy comparisons. When a timestamp `ts1` occurs before a
92/// timestamp `ts2`, then `dt1 < dt2`. For example:
93///
94/// ```
95/// use jiff::Timestamp;
96///
97/// let ts1 = Timestamp::from_second(123_456_789)?;
98/// let ts2 = Timestamp::from_second(123_456_790)?;
99/// assert!(ts1 < ts2);
100///
101/// # Ok::<(), Box<dyn std::error::Error>>(())
102/// ```
103///
104/// # Arithmetic
105///
106/// This type provides routines for adding and subtracting spans of time, as
107/// well as computing the span of time between two `Timestamp` values.
108///
109/// For adding or subtracting spans of time, one can use any of the following
110/// routines:
111///
112/// * [`Timestamp::checked_add`] or [`Timestamp::checked_sub`] for checked
113/// arithmetic.
114/// * [`Timestamp::saturating_add`] or [`Timestamp::saturating_sub`] for
115/// saturating arithmetic.
116///
117/// Additionally, checked arithmetic is available via the `Add` and `Sub`
118/// trait implementations. When the result overflows, a panic occurs.
119///
120/// ```
121/// use jiff::{Timestamp, ToSpan};
122///
123/// let ts1: Timestamp = "2024-02-25T15:45Z".parse()?;
124/// let ts2 = ts1 - 24.hours();
125/// assert_eq!(ts2.to_string(), "2024-02-24T15:45:00Z");
126///
127/// # Ok::<(), Box<dyn std::error::Error>>(())
128/// ```
129///
130/// One can compute the span of time between two timestamps using either
131/// [`Timestamp::until`] or [`Timestamp::since`]. It's also possible to
132/// subtract two `Timestamp` values directly via a `Sub` trait implementation:
133///
134/// ```
135/// use jiff::{Timestamp, ToSpan};
136///
137/// let ts1: Timestamp = "2024-05-03 23:30:00.123Z".parse()?;
138/// let ts2: Timestamp = "2024-02-25 07Z".parse()?;
139/// // The default is to return spans with units no bigger than seconds.
140/// assert_eq!(ts1 - ts2, 5934600.seconds().milliseconds(123).fieldwise());
141///
142/// # Ok::<(), Box<dyn std::error::Error>>(())
143/// ```
144///
145/// The `until` and `since` APIs are polymorphic and allow re-balancing and
146/// rounding the span returned. For example, the default largest unit is
147/// seconds (as exemplified above), but we can ask for bigger units (up to
148/// hours):
149///
150/// ```
151/// use jiff::{Timestamp, ToSpan, Unit};
152///
153/// let ts1: Timestamp = "2024-05-03 23:30:00.123Z".parse()?;
154/// let ts2: Timestamp = "2024-02-25 07Z".parse()?;
155/// assert_eq!(
156///     // If you want to deal in units bigger than hours, then you'll have to
157///     // convert your timestamp to a [`Zoned`] first.
158///     ts1.since((Unit::Hour, ts2))?,
159///     1648.hours().minutes(30).milliseconds(123).fieldwise(),
160/// );
161///
162/// # Ok::<(), Box<dyn std::error::Error>>(())
163/// ```
164///
165/// You can also round the span returned:
166///
167/// ```
168/// use jiff::{RoundMode, Timestamp, TimestampDifference, ToSpan, Unit};
169///
170/// let ts1: Timestamp = "2024-05-03 23:30:59.123Z".parse()?;
171/// let ts2: Timestamp = "2024-05-02 07Z".parse()?;
172/// assert_eq!(
173///     ts1.since(
174///         TimestampDifference::new(ts2)
175///             .smallest(Unit::Minute)
176///             .largest(Unit::Hour),
177///     )?,
178///     40.hours().minutes(30).fieldwise(),
179/// );
180/// // `TimestampDifference` uses truncation as a rounding mode by default,
181/// // but you can set the rounding mode to break ties away from zero:
182/// assert_eq!(
183///     ts1.since(
184///         TimestampDifference::new(ts2)
185///             .smallest(Unit::Minute)
186///             .largest(Unit::Hour)
187///             .mode(RoundMode::HalfExpand),
188///     )?,
189///     // Rounds up to 31 minutes.
190///     40.hours().minutes(31).fieldwise(),
191/// );
192///
193/// # Ok::<(), Box<dyn std::error::Error>>(())
194/// ```
195///
196/// # Rounding timestamps
197///
198/// A `Timestamp` can be rounded based on a [`TimestampRound`] configuration of
199/// smallest units, rounding increment and rounding mode. Here's an example
200/// showing how to round to the nearest third hour:
201///
202/// ```
203/// use jiff::{Timestamp, TimestampRound, Unit};
204///
205/// let ts: Timestamp = "2024-06-19 16:27:29.999999999Z".parse()?;
206/// assert_eq!(
207///     ts.round(TimestampRound::new().smallest(Unit::Hour).increment(3))?,
208///     "2024-06-19 15Z".parse::<Timestamp>()?,
209/// );
210/// // Or alternatively, make use of the `From<(Unit, i64)> for TimestampRound`
211/// // trait implementation:
212/// assert_eq!(
213///     ts.round((Unit::Hour, 3))?.to_string(),
214///     "2024-06-19T15:00:00Z",
215/// );
216///
217/// # Ok::<(), Box<dyn std::error::Error>>(())
218/// ```
219///
220/// See [`Timestamp::round`] for more details.
221///
222/// # An instant in time
223///
224/// Unlike a [`civil::DateTime`](crate::civil::DateTime), a `Timestamp`
225/// _always_ corresponds, unambiguously, to a precise instant in time (to
226/// nanosecond precision). This means that attaching a time zone to a timestamp
227/// is always unambiguous because there's never any question as to which
228/// instant it refers to. This is true even for gaps in civil time.
229///
230/// For example, in `America/New_York`, clocks were moved ahead one hour
231/// at clock time `2024-03-10 02:00:00`. That is, the 2 o'clock hour never
232/// appeared on clocks in the `America/New_York` region. Since parsing a
233/// timestamp always requires an offset, the time it refers to is unambiguous.
234/// We can see this by writing a clock time, `02:30`, that never existed but
235/// with two different offsets:
236///
237/// ```
238/// use jiff::Timestamp;
239///
240/// // All we're doing here is attaching an offset to a civil datetime.
241/// // There is no time zone information here, and thus there is no
242/// // accounting for ambiguity due to daylight saving time transitions.
243/// let before_hour_jump: Timestamp = "2024-03-10 02:30-04".parse()?;
244/// let after_hour_jump: Timestamp = "2024-03-10 02:30-05".parse()?;
245/// // This shows the instant in time in UTC.
246/// assert_eq!(before_hour_jump.to_string(), "2024-03-10T06:30:00Z");
247/// assert_eq!(after_hour_jump.to_string(), "2024-03-10T07:30:00Z");
248///
249/// // Now let's attach each instant to an `America/New_York` time zone.
250/// let zdt_before = before_hour_jump.in_tz("America/New_York")?;
251/// let zdt_after = after_hour_jump.in_tz("America/New_York")?;
252/// // And now we can see that even though the original instant refers to
253/// // the 2 o'clock hour, since that hour never existed on the clocks in
254/// // `America/New_York`, an instant with a time zone correctly adjusts.
255/// assert_eq!(
256///     zdt_before.to_string(),
257///     "2024-03-10T01:30:00-05:00[America/New_York]",
258/// );
259/// assert_eq!(
260///     zdt_after.to_string(),
261///     "2024-03-10T03:30:00-04:00[America/New_York]",
262/// );
263///
264/// # Ok::<(), Box<dyn std::error::Error>>(())
265/// ```
266///
267/// In the example above, there is never a step that is incorrect or has an
268/// alternative answer. Every step is unambiguous because we never involve
269/// any [`civil`](crate::civil) datetimes.
270///
271/// But note that if the datetime string you're parsing from lacks an offset,
272/// then it *could* be ambiguous even if a time zone is specified. In this
273/// case, parsing will always fail:
274///
275/// ```
276/// use jiff::Timestamp;
277///
278/// let result = "2024-06-30 08:30[America/New_York]".parse::<Timestamp>();
279/// assert_eq!(
280///     result.unwrap_err().to_string(),
281///     "failed to find offset component in \
282///      \"2024-06-30 08:30[America/New_York]\", \
283///      which is required for parsing a timestamp",
284/// );
285/// ```
286///
287/// # Converting a civil datetime to a timestamp
288///
289/// Sometimes you want to convert the "time on the clock" to a precise instant
290/// in time. One way to do this was demonstrated in the previous section, but
291/// it only works if you know your current time zone offset:
292///
293/// ```
294/// use jiff::Timestamp;
295///
296/// let ts: Timestamp = "2024-06-30 08:36-04".parse()?;
297/// assert_eq!(ts.to_string(), "2024-06-30T12:36:00Z");
298///
299/// # Ok::<(), Box<dyn std::error::Error>>(())
300/// ```
301///
302/// The above happened to be the precise instant in time I wrote the example.
303/// Since I happened to know the offset, this worked okay. But what if I
304/// didn't? We could instead construct a civil datetime and attach a time zone
305/// to it. This will create a [`Zoned`] value, from which we can access the
306/// timestamp:
307///
308/// ```
309/// use jiff::civil::date;
310///
311/// let clock = date(2024, 6, 30).at(8, 36, 0, 0).in_tz("America/New_York")?;
312/// assert_eq!(clock.timestamp().to_string(), "2024-06-30T12:36:00Z");
313///
314/// # Ok::<(), Box<dyn std::error::Error>>(())
315/// ```
316#[derive(Clone, Copy)]
317pub struct Timestamp {
318    second: UnixSeconds,
319    nanosecond: FractionalNanosecond,
320}
321
322impl Timestamp {
323    /// The minimum representable timestamp.
324    ///
325    /// The minimum is chosen such that it can be combined with
326    /// any legal [`Offset`](crate::tz::Offset) and turned into a
327    /// [`civil::DateTime`](crate::civil::DateTime).
328    ///
329    /// # Example
330    ///
331    /// ```
332    /// use jiff::{civil::date, tz::Offset, Timestamp};
333    ///
334    /// let dt = Offset::MIN.to_datetime(Timestamp::MIN);
335    /// assert_eq!(dt, date(-9999, 1, 1).at(0, 0, 0, 0));
336    /// ```
337    pub const MIN: Timestamp = Timestamp {
338        second: UnixSeconds::MIN_SELF,
339        nanosecond: FractionalNanosecond::N::<0>(),
340    };
341
342    /// The maximum representable timestamp.
343    ///
344    /// The maximum is chosen such that it can be combined with
345    /// any legal [`Offset`](crate::tz::Offset) and turned into a
346    /// [`civil::DateTime`](crate::civil::DateTime).
347    ///
348    /// # Example
349    ///
350    /// ```
351    /// use jiff::{civil::date, tz::Offset, Timestamp};
352    ///
353    /// let dt = Offset::MAX.to_datetime(Timestamp::MAX);
354    /// assert_eq!(dt, date(9999, 12, 31).at(23, 59, 59, 999_999_999));
355    /// ```
356    pub const MAX: Timestamp = Timestamp {
357        second: UnixSeconds::MAX_SELF,
358        nanosecond: FractionalNanosecond::MAX_SELF,
359    };
360
361    /// The Unix epoch represented as a timestamp.
362    ///
363    /// The Unix epoch corresponds to the instant at `1970-01-01T00:00:00Z`.
364    /// As a timestamp, it corresponds to `0` nanoseconds.
365    ///
366    /// A timestamp is positive if and only if it is greater than the Unix
367    /// epoch. A timestamp is negative if and only if it is less than the Unix
368    /// epoch.
369    pub const UNIX_EPOCH: Timestamp = Timestamp {
370        second: UnixSeconds::N::<0>(),
371        nanosecond: FractionalNanosecond::N::<0>(),
372    };
373
374    /// Returns the current system time as a timestamp.
375    ///
376    /// # Panics
377    ///
378    /// This panics if the system clock is set to a time value outside of the
379    /// range `-009999-01-01T00:00:00Z..=9999-12-31T11:59:59.999999999Z`. The
380    /// justification here is that it is reasonable to expect the system clock
381    /// to be set to a somewhat sane, if imprecise, value.
382    ///
383    /// If you want to get the current Unix time fallibly, use
384    /// [`Timestamp::try_from`] with a `std::time::SystemTime` as input.
385    ///
386    /// This may also panic when `SystemTime::now()` itself panics. The most
387    /// common context in which this happens is on the `wasm32-unknown-unknown`
388    /// target. If you're using that target in the context of the web (for
389    /// example, via `wasm-pack`), and you're an application, then you should
390    /// enable Jiff's `js` feature. This will automatically instruct Jiff in
391    /// this very specific circumstance to execute JavaScript code to determine
392    /// the current time from the web browser.
393    ///
394    /// # Example
395    ///
396    /// ```
397    /// use jiff::Timestamp;
398    ///
399    /// assert!(Timestamp::now() > Timestamp::UNIX_EPOCH);
400    /// ```
401    #[cfg(feature = "std")]
402    pub fn now() -> Timestamp {
403        Timestamp::try_from(crate::now::system_time())
404            .expect("system time is valid")
405    }
406
407    /// Creates a new instant in time represented as a timestamp.
408    ///
409    /// While a timestamp is logically a count of nanoseconds since the Unix
410    /// epoch, this constructor provides a convenience way of constructing
411    /// the timestamp from two components: seconds and fractional seconds
412    /// expressed as nanoseconds.
413    ///
414    /// The signs of `second` and `nanosecond` need not be the same.
415    ///
416    /// # Errors
417    ///
418    /// This returns an error if the given components would correspond to
419    /// an instant outside the supported range. Also, `nanosecond` is limited
420    /// to the range `-999,999,999..=999,999,999`.
421    ///
422    /// # Example
423    ///
424    /// This example shows the instant in time 123,456,789 seconds after the
425    /// Unix epoch:
426    ///
427    /// ```
428    /// use jiff::Timestamp;
429    ///
430    /// assert_eq!(
431    ///     Timestamp::new(123_456_789, 0)?.to_string(),
432    ///     "1973-11-29T21:33:09Z",
433    /// );
434    ///
435    /// # Ok::<(), Box<dyn std::error::Error>>(())
436    /// ```
437    ///
438    /// # Example: normalized sign
439    ///
440    /// This example shows how `second` and `nanosecond` are resolved when
441    /// their signs differ.
442    ///
443    /// ```
444    /// use jiff::Timestamp;
445    ///
446    /// let ts = Timestamp::new(2, -999_999_999)?;
447    /// assert_eq!(ts.as_second(), 1);
448    /// assert_eq!(ts.subsec_nanosecond(), 1);
449    ///
450    /// let ts = Timestamp::new(-2, 999_999_999)?;
451    /// assert_eq!(ts.as_second(), -1);
452    /// assert_eq!(ts.subsec_nanosecond(), -1);
453    ///
454    /// # Ok::<(), Box<dyn std::error::Error>>(())
455    /// ```
456    ///
457    /// # Example: limits
458    ///
459    /// The minimum timestamp has nanoseconds set to zero, while the maximum
460    /// timestamp has nanoseconds set to `999,999,999`:
461    ///
462    /// ```
463    /// use jiff::Timestamp;
464    ///
465    /// assert_eq!(Timestamp::MIN.subsec_nanosecond(), 0);
466    /// assert_eq!(Timestamp::MAX.subsec_nanosecond(), 999_999_999);
467    /// ```
468    ///
469    /// As a consequence, nanoseconds cannot be negative when a timestamp has
470    /// minimal seconds:
471    ///
472    /// ```
473    /// use jiff::Timestamp;
474    ///
475    /// assert!(Timestamp::new(Timestamp::MIN.as_second(), -1).is_err());
476    /// // But they can be positive!
477    /// let one_ns_more = Timestamp::new(Timestamp::MIN.as_second(), 1)?;
478    /// assert_eq!(
479    ///     one_ns_more.to_string(),
480    ///     "-009999-01-02T01:59:59.000000001Z",
481    /// );
482    /// // Or, when combined with a minimal offset:
483    /// assert_eq!(
484    ///     jiff::tz::Offset::MIN.to_datetime(one_ns_more).to_string(),
485    ///     "-009999-01-01T00:00:00.000000001",
486    /// );
487    ///
488    /// # Ok::<(), Box<dyn std::error::Error>>(())
489    /// ```
490    #[inline]
491    pub fn new(second: i64, nanosecond: i32) -> Result<Timestamp, Error> {
492        Timestamp::new_ranged(
493            UnixSeconds::try_new("second", second)?,
494            FractionalNanosecond::try_new("nanosecond", nanosecond)?,
495        )
496    }
497
498    /// Creates a new `Timestamp` value in a `const` context.
499    ///
500    /// # Panics
501    ///
502    /// This routine panics when [`Timestamp::new`] would return an error.
503    /// That is, when the given components would correspond to
504    /// an instant outside the supported range. Also, `nanosecond` is limited
505    /// to the range `-999,999,999..=999,999,999`.
506    ///
507    /// # Example
508    ///
509    /// This example shows the instant in time 123,456,789 seconds after the
510    /// Unix epoch:
511    ///
512    /// ```
513    /// use jiff::Timestamp;
514    ///
515    /// assert_eq!(
516    ///     Timestamp::constant(123_456_789, 0).to_string(),
517    ///     "1973-11-29T21:33:09Z",
518    /// );
519    /// ```
520    #[inline]
521    pub const fn constant(mut second: i64, mut nanosecond: i32) -> Timestamp {
522        if second == UnixSeconds::MIN_REPR && nanosecond < 0 {
523            panic!("nanoseconds must be >=0 when seconds are minimal");
524        }
525        // We now normalize our seconds and nanoseconds such that they have
526        // the same sign (or where one is zero). So for example, when given
527        // `-1s 1ns`, then we should turn that into `-999,999,999ns`.
528        //
529        // But first, if we're already normalized, we're done!
530        if second.signum() as i8 == nanosecond.signum() as i8
531            || second == 0
532            || nanosecond == 0
533        {
534            return Timestamp {
535                second: UnixSeconds::new_unchecked(second),
536                nanosecond: FractionalNanosecond::new_unchecked(nanosecond),
537            };
538        }
539        if second < 0 && nanosecond > 0 {
540            second += 1;
541            nanosecond -= t::NANOS_PER_SECOND.value() as i32;
542        } else if second > 0 && nanosecond < 0 {
543            second -= 1;
544            nanosecond += t::NANOS_PER_SECOND.value() as i32;
545        }
546        Timestamp {
547            second: UnixSeconds::new_unchecked(second),
548            nanosecond: FractionalNanosecond::new_unchecked(nanosecond),
549        }
550    }
551
552    /// Creates a new instant in time from the number of seconds elapsed since
553    /// the Unix epoch.
554    ///
555    /// When `second` is negative, it corresponds to an instant in time before
556    /// the Unix epoch. A smaller number corresponds to an instant in time
557    /// further into the past.
558    ///
559    /// # Errors
560    ///
561    /// This returns an error if the given second corresponds to a timestamp
562    /// outside of the [`Timestamp::MIN`] and [`Timestamp::MAX`] boundaries.
563    ///
564    /// # Example
565    ///
566    /// This example shows the instants in time 1 second immediately after and
567    /// before the Unix epoch:
568    ///
569    /// ```
570    /// use jiff::Timestamp;
571    ///
572    /// assert_eq!(
573    ///     Timestamp::from_second(1)?.to_string(),
574    ///     "1970-01-01T00:00:01Z",
575    /// );
576    /// assert_eq!(
577    ///     Timestamp::from_second(-1)?.to_string(),
578    ///     "1969-12-31T23:59:59Z",
579    /// );
580    ///
581    /// # Ok::<(), Box<dyn std::error::Error>>(())
582    /// ```
583    #[inline]
584    pub fn from_second(second: i64) -> Result<Timestamp, Error> {
585        Timestamp::new(second, 0)
586    }
587
588    /// Creates a new instant in time from the number of milliseconds elapsed
589    /// since the Unix epoch.
590    ///
591    /// When `millisecond` is negative, it corresponds to an instant in time
592    /// before the Unix epoch. A smaller number corresponds to an instant in
593    /// time further into the past.
594    ///
595    /// # Errors
596    ///
597    /// This returns an error if the given millisecond corresponds to a
598    /// timestamp outside of the [`Timestamp::MIN`] and [`Timestamp::MAX`]
599    /// boundaries.
600    ///
601    /// # Example
602    ///
603    /// This example shows the instants in time 1 millisecond immediately after
604    /// and before the Unix epoch:
605    ///
606    /// ```
607    /// use jiff::Timestamp;
608    ///
609    /// assert_eq!(
610    ///     Timestamp::from_millisecond(1)?.to_string(),
611    ///     "1970-01-01T00:00:00.001Z",
612    /// );
613    /// assert_eq!(
614    ///     Timestamp::from_millisecond(-1)?.to_string(),
615    ///     "1969-12-31T23:59:59.999Z",
616    /// );
617    ///
618    /// # Ok::<(), Box<dyn std::error::Error>>(())
619    /// ```
620    #[inline]
621    pub fn from_millisecond(millisecond: i64) -> Result<Timestamp, Error> {
622        let millisecond = UnixMilliseconds::try_new128(
623            "millisecond timestamp",
624            millisecond,
625        )?;
626        Ok(Timestamp::from_millisecond_ranged(millisecond))
627    }
628
629    /// Creates a new instant in time from the number of microseconds elapsed
630    /// since the Unix epoch.
631    ///
632    /// When `microsecond` is negative, it corresponds to an instant in time
633    /// before the Unix epoch. A smaller number corresponds to an instant in
634    /// time further into the past.
635    ///
636    /// # Errors
637    ///
638    /// This returns an error if the given microsecond corresponds to a
639    /// timestamp outside of the [`Timestamp::MIN`] and [`Timestamp::MAX`]
640    /// boundaries.
641    ///
642    /// # Example
643    ///
644    /// This example shows the instants in time 1 microsecond immediately after
645    /// and before the Unix epoch:
646    ///
647    /// ```
648    /// use jiff::Timestamp;
649    ///
650    /// assert_eq!(
651    ///     Timestamp::from_microsecond(1)?.to_string(),
652    ///     "1970-01-01T00:00:00.000001Z",
653    /// );
654    /// assert_eq!(
655    ///     Timestamp::from_microsecond(-1)?.to_string(),
656    ///     "1969-12-31T23:59:59.999999Z",
657    /// );
658    ///
659    /// # Ok::<(), Box<dyn std::error::Error>>(())
660    /// ```
661    #[inline]
662    pub fn from_microsecond(microsecond: i64) -> Result<Timestamp, Error> {
663        let microsecond = UnixMicroseconds::try_new128(
664            "microsecond timestamp",
665            microsecond,
666        )?;
667        Ok(Timestamp::from_microsecond_ranged(microsecond))
668    }
669
670    /// Creates a new instant in time from the number of nanoseconds elapsed
671    /// since the Unix epoch.
672    ///
673    /// When `nanosecond` is negative, it corresponds to an instant in time
674    /// before the Unix epoch. A smaller number corresponds to an instant in
675    /// time further into the past.
676    ///
677    /// # Errors
678    ///
679    /// This returns an error if the given nanosecond corresponds to a
680    /// timestamp outside of the [`Timestamp::MIN`] and [`Timestamp::MAX`]
681    /// boundaries.
682    ///
683    /// # Example
684    ///
685    /// This example shows the instants in time 1 nanosecond immediately after
686    /// and before the Unix epoch:
687    ///
688    /// ```
689    /// use jiff::Timestamp;
690    ///
691    /// assert_eq!(
692    ///     Timestamp::from_nanosecond(1)?.to_string(),
693    ///     "1970-01-01T00:00:00.000000001Z",
694    /// );
695    /// assert_eq!(
696    ///     Timestamp::from_nanosecond(-1)?.to_string(),
697    ///     "1969-12-31T23:59:59.999999999Z",
698    /// );
699    ///
700    /// # Ok::<(), Box<dyn std::error::Error>>(())
701    /// ```
702    #[inline]
703    pub fn from_nanosecond(nanosecond: i128) -> Result<Timestamp, Error> {
704        let nanosecond =
705            UnixNanoseconds::try_new128("nanosecond timestamp", nanosecond)?;
706        Ok(Timestamp::from_nanosecond_ranged(nanosecond))
707    }
708
709    /// Creates a new timestamp from a `Duration` with the given sign since the
710    /// Unix epoch.
711    ///
712    /// Positive durations result in a timestamp after the Unix epoch. Negative
713    /// durations result in a timestamp before the Unix epoch.
714    ///
715    /// # Errors
716    ///
717    /// This returns an error if the given duration corresponds to a timestamp
718    /// outside of the [`Timestamp::MIN`] and [`Timestamp::MAX`] boundaries.
719    ///
720    /// # Example
721    ///
722    /// How one might construct a `Timestamp` from a `SystemTime`:
723    ///
724    /// ```
725    /// use std::time::SystemTime;
726    /// use jiff::{SignedDuration, Timestamp};
727    ///
728    /// let unix_epoch = SystemTime::UNIX_EPOCH;
729    /// let now = SystemTime::now();
730    /// let duration = SignedDuration::system_until(unix_epoch, now)?;
731    /// let ts = Timestamp::from_duration(duration)?;
732    /// assert!(ts > Timestamp::UNIX_EPOCH);
733    ///
734    /// # Ok::<(), Box<dyn std::error::Error>>(())
735    /// ```
736    ///
737    /// Of course, one should just use [`Timestamp::try_from`] for this
738    /// instead. Indeed, the above example is copied almost exactly from the
739    /// `TryFrom` implementation.
740    ///
741    /// # Example: out of bounds
742    ///
743    /// This example shows how some of the boundary conditions are dealt with.
744    ///
745    /// ```
746    /// use jiff::{SignedDuration, Timestamp};
747    ///
748    /// // OK, we get the minimum timestamp supported by Jiff:
749    /// let duration = SignedDuration::new(-377705023201, 0);
750    /// let ts = Timestamp::from_duration(duration)?;
751    /// assert_eq!(ts, Timestamp::MIN);
752    ///
753    /// // We use the minimum number of seconds, but even subtracting
754    /// // one more nanosecond after it will result in an error.
755    /// let duration = SignedDuration::new(-377705023201, -1);
756    /// assert_eq!(
757    ///     Timestamp::from_duration(duration).unwrap_err().to_string(),
758    ///     "parameter 'seconds and nanoseconds' with value -1 is not \
759    ///      in the required range of 0..=1000000000",
760    /// );
761    ///
762    /// # Ok::<(), Box<dyn std::error::Error>>(())
763    /// ```
764    #[inline]
765    pub fn from_duration(
766        duration: SignedDuration,
767    ) -> Result<Timestamp, Error> {
768        // As an optimization, we don't need to go through `Timestamp::new`
769        // (or `Timestamp::new_ranged`) here. That's because a `SignedDuration`
770        // already guarantees that its seconds and nanoseconds are "coherent."
771        // That is, we know we can't have a negative second with a positive
772        // nanosecond (or vice versa).
773        let second = UnixSeconds::try_new("second", duration.as_secs())?;
774        let nanosecond = FractionalNanosecond::try_new(
775            "nanosecond",
776            duration.subsec_nanos(),
777        )?;
778        // ... but we do have to check that the *combination* of seconds and
779        // nanoseconds aren't out of bounds, which is possible even when both
780        // are, on their own, legal values.
781        if second == UnixSeconds::MIN_REPR && nanosecond < 0 {
782            return Err(Error::range(
783                "seconds and nanoseconds",
784                nanosecond,
785                0,
786                1_000_000_000,
787            ));
788        }
789        Ok(Timestamp { second, nanosecond })
790    }
791
792    /// Returns this timestamp as a number of seconds since the Unix epoch.
793    ///
794    /// This only returns the number of whole seconds. That is, if there are
795    /// any fractional seconds in this timestamp, then they are truncated.
796    ///
797    /// # Example
798    ///
799    /// ```
800    /// use jiff::Timestamp;
801    ///
802    /// let ts = Timestamp::new(5, 123_456_789)?;
803    /// assert_eq!(ts.as_second(), 5);
804    /// let ts = Timestamp::new(5, 999_999_999)?;
805    /// assert_eq!(ts.as_second(), 5);
806    ///
807    /// let ts = Timestamp::new(-5, -123_456_789)?;
808    /// assert_eq!(ts.as_second(), -5);
809    /// let ts = Timestamp::new(-5, -999_999_999)?;
810    /// assert_eq!(ts.as_second(), -5);
811    ///
812    /// # Ok::<(), Box<dyn std::error::Error>>(())
813    /// ```
814    #[inline]
815    pub fn as_second(self) -> i64 {
816        self.as_second_ranged().get()
817    }
818
819    /// Returns this timestamp as a number of milliseconds since the Unix
820    /// epoch.
821    ///
822    /// This only returns the number of whole milliseconds. That is, if there
823    /// are any fractional milliseconds in this timestamp, then they are
824    /// truncated.
825    ///
826    /// # Example
827    ///
828    /// ```
829    /// use jiff::Timestamp;
830    ///
831    /// let ts = Timestamp::new(5, 123_456_789)?;
832    /// assert_eq!(ts.as_millisecond(), 5_123);
833    /// let ts = Timestamp::new(5, 999_999_999)?;
834    /// assert_eq!(ts.as_millisecond(), 5_999);
835    ///
836    /// let ts = Timestamp::new(-5, -123_456_789)?;
837    /// assert_eq!(ts.as_millisecond(), -5_123);
838    /// let ts = Timestamp::new(-5, -999_999_999)?;
839    /// assert_eq!(ts.as_millisecond(), -5_999);
840    ///
841    /// # Ok::<(), Box<dyn std::error::Error>>(())
842    /// ```
843    #[inline]
844    pub fn as_millisecond(self) -> i64 {
845        self.as_millisecond_ranged().get()
846    }
847
848    /// Returns this timestamp as a number of microseconds since the Unix
849    /// epoch.
850    ///
851    /// This only returns the number of whole microseconds. That is, if there
852    /// are any fractional microseconds in this timestamp, then they are
853    /// truncated.
854    ///
855    /// # Example
856    ///
857    /// ```
858    /// use jiff::Timestamp;
859    ///
860    /// let ts = Timestamp::new(5, 123_456_789)?;
861    /// assert_eq!(ts.as_microsecond(), 5_123_456);
862    /// let ts = Timestamp::new(5, 999_999_999)?;
863    /// assert_eq!(ts.as_microsecond(), 5_999_999);
864    ///
865    /// let ts = Timestamp::new(-5, -123_456_789)?;
866    /// assert_eq!(ts.as_microsecond(), -5_123_456);
867    /// let ts = Timestamp::new(-5, -999_999_999)?;
868    /// assert_eq!(ts.as_microsecond(), -5_999_999);
869    ///
870    /// # Ok::<(), Box<dyn std::error::Error>>(())
871    /// ```
872    #[inline]
873    pub fn as_microsecond(self) -> i64 {
874        self.as_microsecond_ranged().get()
875    }
876
877    /// Returns this timestamp as a number of nanoseconds since the Unix
878    /// epoch.
879    ///
880    /// Since a `Timestamp` has a nanosecond precision, the nanoseconds
881    /// returned here represent this timestamp losslessly. That is, the
882    /// nanoseconds returned can be used with [`Timestamp::from_nanosecond`] to
883    /// create an identical timestamp with no loss of precision.
884    ///
885    /// # Example
886    ///
887    /// ```
888    /// use jiff::Timestamp;
889    ///
890    /// let ts = Timestamp::new(5, 123_456_789)?;
891    /// assert_eq!(ts.as_nanosecond(), 5_123_456_789);
892    /// let ts = Timestamp::new(5, 999_999_999)?;
893    /// assert_eq!(ts.as_nanosecond(), 5_999_999_999);
894    ///
895    /// let ts = Timestamp::new(-5, -123_456_789)?;
896    /// assert_eq!(ts.as_nanosecond(), -5_123_456_789);
897    /// let ts = Timestamp::new(-5, -999_999_999)?;
898    /// assert_eq!(ts.as_nanosecond(), -5_999_999_999);
899    ///
900    /// # Ok::<(), Box<dyn std::error::Error>>(())
901    /// ```
902    #[inline]
903    pub fn as_nanosecond(self) -> i128 {
904        self.as_nanosecond_ranged().get()
905    }
906
907    /// Returns the fractional second component of this timestamp in units
908    /// of milliseconds.
909    ///
910    /// It is guaranteed that this will never return a value that is greater
911    /// than 1 second (or less than -1 second).
912    ///
913    /// This only returns the number of whole milliseconds. That is, if there
914    /// are any fractional milliseconds in this timestamp, then they are
915    /// truncated.
916    ///
917    /// # Example
918    ///
919    /// ```
920    /// use jiff::Timestamp;
921    ///
922    /// let ts = Timestamp::new(5, 123_456_789)?;
923    /// assert_eq!(ts.subsec_millisecond(), 123);
924    /// let ts = Timestamp::new(5, 999_999_999)?;
925    /// assert_eq!(ts.subsec_millisecond(), 999);
926    ///
927    /// let ts = Timestamp::new(-5, -123_456_789)?;
928    /// assert_eq!(ts.subsec_millisecond(), -123);
929    /// let ts = Timestamp::new(-5, -999_999_999)?;
930    /// assert_eq!(ts.subsec_millisecond(), -999);
931    ///
932    /// # Ok::<(), Box<dyn std::error::Error>>(())
933    /// ```
934    #[inline]
935    pub fn subsec_millisecond(self) -> i32 {
936        self.subsec_millisecond_ranged().get()
937    }
938
939    /// Returns the fractional second component of this timestamp in units of
940    /// microseconds.
941    ///
942    /// It is guaranteed that this will never return a value that is greater
943    /// than 1 second (or less than -1 second).
944    ///
945    /// This only returns the number of whole microseconds. That is, if there
946    /// are any fractional microseconds in this timestamp, then they are
947    /// truncated.
948    ///
949    /// # Example
950    ///
951    /// ```
952    /// use jiff::Timestamp;
953    ///
954    /// let ts = Timestamp::new(5, 123_456_789)?;
955    /// assert_eq!(ts.subsec_microsecond(), 123_456);
956    /// let ts = Timestamp::new(5, 999_999_999)?;
957    /// assert_eq!(ts.subsec_microsecond(), 999_999);
958    ///
959    /// let ts = Timestamp::new(-5, -123_456_789)?;
960    /// assert_eq!(ts.subsec_microsecond(), -123_456);
961    /// let ts = Timestamp::new(-5, -999_999_999)?;
962    /// assert_eq!(ts.subsec_microsecond(), -999_999);
963    ///
964    /// # Ok::<(), Box<dyn std::error::Error>>(())
965    /// ```
966    #[inline]
967    pub fn subsec_microsecond(self) -> i32 {
968        self.subsec_microsecond_ranged().get()
969    }
970
971    /// Returns the fractional second component of this timestamp in units of
972    /// nanoseconds.
973    ///
974    /// It is guaranteed that this will never return a value that is greater
975    /// than 1 second (or less than -1 second).
976    ///
977    /// # Example
978    ///
979    /// ```
980    /// use jiff::Timestamp;
981    ///
982    /// let ts = Timestamp::new(5, 123_456_789)?;
983    /// assert_eq!(ts.subsec_nanosecond(), 123_456_789);
984    /// let ts = Timestamp::new(5, 999_999_999)?;
985    /// assert_eq!(ts.subsec_nanosecond(), 999_999_999);
986    ///
987    /// let ts = Timestamp::new(-5, -123_456_789)?;
988    /// assert_eq!(ts.subsec_nanosecond(), -123_456_789);
989    /// let ts = Timestamp::new(-5, -999_999_999)?;
990    /// assert_eq!(ts.subsec_nanosecond(), -999_999_999);
991    ///
992    /// # Ok::<(), Box<dyn std::error::Error>>(())
993    /// ```
994    #[inline]
995    pub fn subsec_nanosecond(self) -> i32 {
996        self.subsec_nanosecond_ranged().get()
997    }
998
999    /// Returns this timestamp as a [`SignedDuration`] since the Unix epoch.
1000    ///
1001    /// # Example
1002    ///
1003    /// ```
1004    /// use jiff::{SignedDuration, Timestamp};
1005    ///
1006    /// assert_eq!(
1007    ///     Timestamp::UNIX_EPOCH.as_duration(),
1008    ///     SignedDuration::ZERO,
1009    /// );
1010    /// assert_eq!(
1011    ///     Timestamp::new(5, 123_456_789)?.as_duration(),
1012    ///     SignedDuration::new(5, 123_456_789),
1013    /// );
1014    /// assert_eq!(
1015    ///     Timestamp::new(-5, -123_456_789)?.as_duration(),
1016    ///     SignedDuration::new(-5, -123_456_789),
1017    /// );
1018    ///
1019    /// # Ok::<(), Box<dyn std::error::Error>>(())
1020    /// ```
1021    #[inline]
1022    pub fn as_duration(self) -> SignedDuration {
1023        SignedDuration::from_timestamp(self)
1024    }
1025
1026    /// Returns the sign of this timestamp.
1027    ///
1028    /// This can return one of three possible values:
1029    ///
1030    /// * `0` when this timestamp is precisely equivalent to
1031    /// [`Timestamp::UNIX_EPOCH`].
1032    /// * `1` when this timestamp occurs after the Unix epoch.
1033    /// * `-1` when this timestamp occurs before the Unix epoch.
1034    ///
1035    /// The sign returned is guaranteed to match the sign of all "getter"
1036    /// methods on `Timestamp`. For example, [`Timestamp::as_second`] and
1037    /// [`Timestamp::subsec_nanosecond`]. This is true even if the signs
1038    /// of the `second` and `nanosecond` components were mixed when given to
1039    /// the [`Timestamp::new`] constructor.
1040    ///
1041    /// # Example
1042    ///
1043    /// ```
1044    /// use jiff::Timestamp;
1045    ///
1046    /// let ts = Timestamp::new(5, -999_999_999)?;
1047    /// assert_eq!(ts.signum(), 1);
1048    /// // The mixed signs were normalized away!
1049    /// assert_eq!(ts.as_second(), 4);
1050    /// assert_eq!(ts.subsec_nanosecond(), 1);
1051    ///
1052    /// // The same applies for negative timestamps.
1053    /// let ts = Timestamp::new(-5, 999_999_999)?;
1054    /// assert_eq!(ts.signum(), -1);
1055    /// assert_eq!(ts.as_second(), -4);
1056    /// assert_eq!(ts.subsec_nanosecond(), -1);
1057    ///
1058    /// # Ok::<(), Box<dyn std::error::Error>>(())
1059    /// ```
1060    #[inline]
1061    pub fn signum(self) -> i8 {
1062        if self.is_zero() {
1063            0
1064        } else if self.as_second() > 0 || self.subsec_nanosecond() > 0 {
1065            1
1066        } else {
1067            -1
1068        }
1069    }
1070
1071    /// Returns true if and only if this timestamp corresponds to the instant
1072    /// in time known as the Unix epoch.
1073    ///
1074    /// # Example
1075    ///
1076    /// ```
1077    /// use jiff::Timestamp;
1078    ///
1079    /// assert!(Timestamp::UNIX_EPOCH.is_zero());
1080    /// ```
1081    #[inline]
1082    pub fn is_zero(self) -> bool {
1083        self.as_second() == 0 && self.subsec_nanosecond() == 0
1084    }
1085
1086    /// Creates a [`Zoned`] value by attaching a time zone for the given name
1087    /// to this instant in time.
1088    ///
1089    /// The name given is resolved to a [`TimeZone`] by using the default
1090    /// [`TimeZoneDatabase`](crate::tz::TimeZoneDatabase) created by
1091    /// [`tz::db`](crate::tz::db). Indeed, this is a convenience function
1092    /// for [`Timestamp::to_zoned`] where the time zone database lookup
1093    /// is done automatically.
1094    ///
1095    /// Assuming the time zone name could be resolved to a [`TimeZone`], this
1096    /// routine is otherwise infallible and never results in any ambiguity
1097    /// since both a [`Timestamp`] and a [`Zoned`] correspond to precise
1098    /// instant in time. This is unlike
1099    /// [`civil::DateTime::to_zoned`](crate::civil::DateTime::to_zoned),
1100    /// where a civil datetime might correspond to more than one instant in
1101    /// time (i.e., a fold, typically DST ending) or no instants in time (i.e.,
1102    /// a gap, typically DST starting).
1103    ///
1104    /// # Errors
1105    ///
1106    /// This returns an error when the given time zone name could not be found
1107    /// in the default time zone database.
1108    ///
1109    /// # Example
1110    ///
1111    /// This is a simple example of converting the instant that is `123,456,789`
1112    /// seconds after the Unix epoch to an instant that is aware of its time
1113    /// zone:
1114    ///
1115    /// ```
1116    /// use jiff::Timestamp;
1117    ///
1118    /// let ts = Timestamp::new(123_456_789, 0).unwrap();
1119    /// let zdt = ts.in_tz("America/New_York")?;
1120    /// assert_eq!(zdt.to_string(), "1973-11-29T16:33:09-05:00[America/New_York]");
1121    ///
1122    /// # Ok::<(), Box<dyn std::error::Error>>(())
1123    /// ```
1124    ///
1125    /// This can be used to answer questions like, "What time was it at the
1126    /// Unix epoch in Tasmania?"
1127    ///
1128    /// ```
1129    /// use jiff::Timestamp;
1130    ///
1131    /// // Time zone database lookups are case insensitive!
1132    /// let zdt = Timestamp::UNIX_EPOCH.in_tz("australia/tasmania")?;
1133    /// assert_eq!(zdt.to_string(), "1970-01-01T11:00:00+11:00[Australia/Tasmania]");
1134    ///
1135    /// # Ok::<(), Box<dyn std::error::Error>>(())
1136    /// ```
1137    ///
1138    /// # Example: errors
1139    ///
1140    /// This routine can return an error when the time zone is unrecognized:
1141    ///
1142    /// ```
1143    /// use jiff::Timestamp;
1144    ///
1145    /// assert!(Timestamp::UNIX_EPOCH.in_tz("does not exist").is_err());
1146    /// ```
1147    #[inline]
1148    pub fn in_tz(self, time_zone_name: &str) -> Result<Zoned, Error> {
1149        let tz = crate::tz::db().get(time_zone_name)?;
1150        Ok(self.to_zoned(tz))
1151    }
1152
1153    /// Creates a [`Zoned`] value by attaching the given time zone to this
1154    /// instant in time.
1155    ///
1156    /// This is infallible and never results in any ambiguity since both a
1157    /// [`Timestamp`] and a [`Zoned`] correspond to precise instant in time.
1158    /// This is unlike
1159    /// [`civil::DateTime::to_zoned`](crate::civil::DateTime::to_zoned),
1160    /// where a civil datetime might correspond to more than one instant in
1161    /// time (i.e., a fold, typically DST ending) or no instants in time (i.e.,
1162    /// a gap, typically DST starting).
1163    ///
1164    /// In the common case of a time zone being represented as a name string,
1165    /// like `Australia/Tasmania`, consider using [`Timestamp::in_tz`]
1166    /// instead.
1167    ///
1168    /// # Example
1169    ///
1170    /// This example shows how to create a zoned value with a fixed time zone
1171    /// offset:
1172    ///
1173    /// ```
1174    /// use jiff::{tz::{self, TimeZone}, Timestamp};
1175    ///
1176    /// let ts = Timestamp::new(123_456_789, 0).unwrap();
1177    /// let tz = TimeZone::fixed(tz::offset(-4));
1178    /// let zdt = ts.to_zoned(tz);
1179    /// // A time zone annotation is still included in the printable version
1180    /// // of the Zoned value, but it is fixed to a particular offset.
1181    /// assert_eq!(zdt.to_string(), "1973-11-29T17:33:09-04:00[-04:00]");
1182    /// ```
1183    ///
1184    /// # Example: POSIX time zone strings
1185    ///
1186    /// This example shows how to create a time zone from a POSIX time zone
1187    /// string that describes the transition to and from daylight saving
1188    /// time for `America/St_Johns`. In particular, this rule uses non-zero
1189    /// minutes, which is atypical.
1190    ///
1191    /// ```
1192    /// use jiff::{tz::TimeZone, Timestamp};
1193    ///
1194    /// let ts = Timestamp::new(123_456_789, 0)?;
1195    /// let tz = TimeZone::posix("NST3:30NDT,M3.2.0,M11.1.0")?;
1196    /// let zdt = ts.to_zoned(tz);
1197    /// // There isn't any agreed upon mechanism for transmitting a POSIX time
1198    /// // zone string within an RFC 9557 TZ annotation, so Jiff just emits the
1199    /// // offset. In practice, POSIX TZ strings are rarely user facing anyway.
1200    /// // (They are still in widespread use as an implementation detail of the
1201    /// // IANA Time Zone Database however.)
1202    /// assert_eq!(zdt.to_string(), "1973-11-29T18:03:09-03:30[-03:30]");
1203    ///
1204    /// # Ok::<(), Box<dyn std::error::Error>>(())
1205    /// ```
1206    #[inline]
1207    pub fn to_zoned(self, tz: TimeZone) -> Zoned {
1208        Zoned::new(self, tz)
1209    }
1210
1211    /// Add the given span of time to this timestamp.
1212    ///
1213    /// This operation accepts three different duration types: [`Span`],
1214    /// [`SignedDuration`] or [`std::time::Duration`]. This is achieved via
1215    /// `From` trait implementations for the [`TimestampArithmetic`] type.
1216    ///
1217    /// # Properties
1218    ///
1219    /// Given a timestamp `ts1` and a span `s`, and assuming `ts2 = ts1 + s`
1220    /// exists, it follows then that `ts1 = ts2 - s` for all values of `ts1`
1221    /// and `s` that sum to a valid `ts2`.
1222    ///
1223    /// In short, subtracting the given span from the sum returned by this
1224    /// function is guaranteed to result in precisely the original timestamp.
1225    ///
1226    /// # Errors
1227    ///
1228    /// If the sum would overflow the minimum or maximum timestamp values, then
1229    /// an error is returned.
1230    ///
1231    /// This also returns an error if the given duration is a `Span` with any
1232    /// non-zero units greater than hours. If you want to use bigger units,
1233    /// convert this timestamp to a `Zoned` and use [`Zoned::checked_add`].
1234    /// This error occurs because a `Timestamp` has no time zone attached to
1235    /// it, and thus cannot unambiguously resolve the length of a single day.
1236    ///
1237    /// # Example
1238    ///
1239    /// This shows how to add `5` hours to the Unix epoch:
1240    ///
1241    /// ```
1242    /// use jiff::{Timestamp, ToSpan};
1243    ///
1244    /// let ts = Timestamp::UNIX_EPOCH.checked_add(5.hours())?;
1245    /// assert_eq!(ts.to_string(), "1970-01-01T05:00:00Z");
1246    ///
1247    /// # Ok::<(), Box<dyn std::error::Error>>(())
1248    /// ```
1249    ///
1250    /// # Example: negative spans are supported
1251    ///
1252    /// This shows how to add `-5` hours to the Unix epoch. This is the same
1253    /// as subtracting `5` hours from the Unix epoch.
1254    ///
1255    /// ```
1256    /// use jiff::{Timestamp, ToSpan};
1257    ///
1258    /// let ts = Timestamp::UNIX_EPOCH.checked_add(-5.hours())?;
1259    /// assert_eq!(ts.to_string(), "1969-12-31T19:00:00Z");
1260    ///
1261    /// # Ok::<(), Box<dyn std::error::Error>>(())
1262    /// ```
1263    ///
1264    /// # Example: available via addition operator
1265    ///
1266    /// This routine can be used via the `+` operator. Note though that if it
1267    /// fails, it will result in a panic.
1268    ///
1269    /// ```
1270    /// use jiff::{Timestamp, ToSpan};
1271    ///
1272    /// let ts1 = Timestamp::new(2_999_999_999, 0)?;
1273    /// assert_eq!(ts1.to_string(), "2065-01-24T05:19:59Z");
1274    ///
1275    /// let ts2 = ts1 + 1.hour().minutes(30).nanoseconds(123);
1276    /// assert_eq!(ts2.to_string(), "2065-01-24T06:49:59.000000123Z");
1277    ///
1278    /// # Ok::<(), Box<dyn std::error::Error>>(())
1279    /// ```
1280    ///
1281    /// # Example: error on overflow
1282    ///
1283    /// ```
1284    /// use jiff::{Timestamp, ToSpan};
1285    ///
1286    /// let ts = Timestamp::MAX;
1287    /// assert_eq!(ts.to_string(), "9999-12-30T22:00:00.999999999Z");
1288    /// assert!(ts.checked_add(1.nanosecond()).is_err());
1289    ///
1290    /// let ts = Timestamp::MIN;
1291    /// assert_eq!(ts.to_string(), "-009999-01-02T01:59:59Z");
1292    /// assert!(ts.checked_add(-1.nanosecond()).is_err());
1293    /// ```
1294    ///
1295    /// # Example: adding absolute durations
1296    ///
1297    /// This shows how to add signed and unsigned absolute durations to a
1298    /// `Timestamp`.
1299    ///
1300    /// ```
1301    /// use std::time::Duration;
1302    ///
1303    /// use jiff::{SignedDuration, Timestamp};
1304    ///
1305    /// let ts1 = Timestamp::new(2_999_999_999, 0)?;
1306    /// assert_eq!(ts1.to_string(), "2065-01-24T05:19:59Z");
1307    ///
1308    /// let dur = SignedDuration::new(60 * 60 + 30 * 60, 123);
1309    /// assert_eq!(
1310    ///     ts1.checked_add(dur)?.to_string(),
1311    ///     "2065-01-24T06:49:59.000000123Z",
1312    /// );
1313    ///
1314    /// let dur = Duration::new(60 * 60 + 30 * 60, 123);
1315    /// assert_eq!(
1316    ///     ts1.checked_add(dur)?.to_string(),
1317    ///     "2065-01-24T06:49:59.000000123Z",
1318    /// );
1319    ///
1320    /// # Ok::<(), Box<dyn std::error::Error>>(())
1321    /// ```
1322    #[inline]
1323    pub fn checked_add<A: Into<TimestampArithmetic>>(
1324        self,
1325        duration: A,
1326    ) -> Result<Timestamp, Error> {
1327        let duration: TimestampArithmetic = duration.into();
1328        duration.checked_add(self)
1329    }
1330
1331    #[inline]
1332    fn checked_add_span(self, span: Span) -> Result<Timestamp, Error> {
1333        if let Some(err) = span.smallest_non_time_non_zero_unit_error() {
1334            return Err(err);
1335        }
1336        if span.is_zero() {
1337            return Ok(self);
1338        }
1339        // The common case is probably a span without fractional seconds, so
1340        // we specialize for that since it requires a fair bit less math.
1341        //
1342        // Note that this only works when *both* the span and timestamp lack
1343        // fractional seconds.
1344        if self.subsec_nanosecond_ranged() == 0 {
1345            if let Some(span_seconds) = span.to_invariant_seconds() {
1346                let time_seconds = self.as_second_ranged();
1347                let sum = time_seconds
1348                    .try_checked_add("span", span_seconds)
1349                    .with_context(|| {
1350                    err!("adding {span} to {self} overflowed")
1351                })?;
1352                return Ok(Timestamp::from_second_ranged(sum));
1353            }
1354        }
1355        let time_nanos = self.as_nanosecond_ranged();
1356        let span_nanos = span.to_invariant_nanoseconds();
1357        let sum = time_nanos
1358            .try_checked_add("span", span_nanos)
1359            .with_context(|| err!("adding {span} to {self} overflowed"))?;
1360        Ok(Timestamp::from_nanosecond_ranged(sum))
1361    }
1362
1363    #[inline]
1364    fn checked_add_duration(
1365        self,
1366        duration: SignedDuration,
1367    ) -> Result<Timestamp, Error> {
1368        let start = self.as_duration();
1369        let end = start.checked_add(duration).ok_or_else(|| {
1370            err!("overflow when adding {duration:?} to {self}")
1371        })?;
1372        Timestamp::from_duration(end)
1373    }
1374
1375    /// This routine is identical to [`Timestamp::checked_add`] with the
1376    /// duration negated.
1377    ///
1378    /// # Errors
1379    ///
1380    /// This has the same error conditions as [`Timestamp::checked_add`].
1381    ///
1382    /// # Example
1383    ///
1384    /// This routine can be used via the `-` operator. Note though that if it
1385    /// fails, it will result in a panic.
1386    ///
1387    /// ```
1388    /// use jiff::{SignedDuration, Timestamp, ToSpan};
1389    ///
1390    /// let ts1 = Timestamp::new(2_999_999_999, 0)?;
1391    /// assert_eq!(ts1.to_string(), "2065-01-24T05:19:59Z");
1392    ///
1393    /// let ts2 = ts1 - 1.hour().minutes(30).nanoseconds(123);
1394    /// assert_eq!(ts2.to_string(), "2065-01-24T03:49:58.999999877Z");
1395    ///
1396    /// # Ok::<(), Box<dyn std::error::Error>>(())
1397    /// ```
1398    ///
1399    /// # Example: use with [`SignedDuration`] and [`std::time::Duration`]
1400    ///
1401    /// ```
1402    /// use std::time::Duration;
1403    ///
1404    /// use jiff::{SignedDuration, Timestamp};
1405    ///
1406    /// let ts1 = Timestamp::new(2_999_999_999, 0)?;
1407    /// assert_eq!(ts1.to_string(), "2065-01-24T05:19:59Z");
1408    ///
1409    /// let dur = SignedDuration::new(60 * 60 + 30 * 60, 123);
1410    /// assert_eq!(
1411    ///     ts1.checked_sub(dur)?.to_string(),
1412    ///     "2065-01-24T03:49:58.999999877Z",
1413    /// );
1414    ///
1415    /// let dur = Duration::new(60 * 60 + 30 * 60, 123);
1416    /// assert_eq!(
1417    ///     ts1.checked_sub(dur)?.to_string(),
1418    ///     "2065-01-24T03:49:58.999999877Z",
1419    /// );
1420    ///
1421    /// # Ok::<(), Box<dyn std::error::Error>>(())
1422    /// ```
1423    #[inline]
1424    pub fn checked_sub<A: Into<TimestampArithmetic>>(
1425        self,
1426        duration: A,
1427    ) -> Result<Timestamp, Error> {
1428        let duration: TimestampArithmetic = duration.into();
1429        duration.checked_neg().and_then(|ta| ta.checked_add(self))
1430    }
1431
1432    /// This routine is identical to [`Timestamp::checked_add`], except the
1433    /// result saturates on overflow. That is, instead of overflow, either
1434    /// [`Timestamp::MIN`] or [`Timestamp::MAX`] is returned.
1435    ///
1436    /// # Errors
1437    ///
1438    /// This returns an error if the given `Span` contains any non-zero units
1439    /// greater than hours.
1440    ///
1441    /// # Example
1442    ///
1443    /// This example shows that arithmetic saturates on overflow.
1444    ///
1445    /// ```
1446    /// use jiff::{SignedDuration, Timestamp, ToSpan};
1447    ///
1448    /// assert_eq!(
1449    ///     Timestamp::MAX,
1450    ///     Timestamp::MAX.saturating_add(1.nanosecond())?,
1451    /// );
1452    /// assert_eq!(
1453    ///     Timestamp::MIN,
1454    ///     Timestamp::MIN.saturating_add(-1.nanosecond())?,
1455    /// );
1456    /// assert_eq!(
1457    ///     Timestamp::MAX,
1458    ///     Timestamp::UNIX_EPOCH.saturating_add(SignedDuration::MAX)?,
1459    /// );
1460    /// assert_eq!(
1461    ///     Timestamp::MIN,
1462    ///     Timestamp::UNIX_EPOCH.saturating_add(SignedDuration::MIN)?,
1463    /// );
1464    /// assert_eq!(
1465    ///     Timestamp::MAX,
1466    ///     Timestamp::UNIX_EPOCH.saturating_add(std::time::Duration::MAX)?,
1467    /// );
1468    ///
1469    /// # Ok::<(), Box<dyn std::error::Error>>(())
1470    /// ```
1471    #[inline]
1472    pub fn saturating_add<A: Into<TimestampArithmetic>>(
1473        self,
1474        duration: A,
1475    ) -> Result<Timestamp, Error> {
1476        let duration: TimestampArithmetic = duration.into();
1477        duration.saturating_add(self).context(
1478            "saturating `Timestamp` arithmetic requires only time units",
1479        )
1480    }
1481
1482    /// This routine is identical to [`Timestamp::saturating_add`] with the
1483    /// span parameter negated.
1484    ///
1485    /// # Errors
1486    ///
1487    /// This returns an error if the given `Span` contains any non-zero units
1488    /// greater than hours.
1489    ///
1490    /// # Example
1491    ///
1492    /// This example shows that arithmetic saturates on overflow.
1493    ///
1494    /// ```
1495    /// use jiff::{SignedDuration, Timestamp, ToSpan};
1496    ///
1497    /// assert_eq!(
1498    ///     Timestamp::MIN,
1499    ///     Timestamp::MIN.saturating_sub(1.nanosecond())?,
1500    /// );
1501    /// assert_eq!(
1502    ///     Timestamp::MAX,
1503    ///     Timestamp::MAX.saturating_sub(-1.nanosecond())?,
1504    /// );
1505    /// assert_eq!(
1506    ///     Timestamp::MIN,
1507    ///     Timestamp::UNIX_EPOCH.saturating_sub(SignedDuration::MAX)?,
1508    /// );
1509    /// assert_eq!(
1510    ///     Timestamp::MAX,
1511    ///     Timestamp::UNIX_EPOCH.saturating_sub(SignedDuration::MIN)?,
1512    /// );
1513    /// assert_eq!(
1514    ///     Timestamp::MIN,
1515    ///     Timestamp::UNIX_EPOCH.saturating_sub(std::time::Duration::MAX)?,
1516    /// );
1517    ///
1518    /// # Ok::<(), Box<dyn std::error::Error>>(())
1519    /// ```
1520    #[inline]
1521    pub fn saturating_sub<A: Into<TimestampArithmetic>>(
1522        self,
1523        duration: A,
1524    ) -> Result<Timestamp, Error> {
1525        let duration: TimestampArithmetic = duration.into();
1526        let Ok(duration) = duration.checked_neg() else {
1527            return Ok(Timestamp::MIN);
1528        };
1529        self.saturating_add(duration)
1530    }
1531
1532    /// Returns a span representing the elapsed time from this timestamp until
1533    /// the given `other` timestamp.
1534    ///
1535    /// When `other` occurs before this timestamp, then the span returned will
1536    /// be negative.
1537    ///
1538    /// Depending on the input provided, the span returned is rounded. It may
1539    /// also be balanced up to bigger units than the default. By default,
1540    /// the span returned is balanced such that the biggest possible unit is
1541    /// seconds.
1542    ///
1543    /// This operation is configured by providing a [`TimestampDifference`]
1544    /// value. Since this routine accepts anything that implements
1545    /// `Into<TimestampDifference>`, once can pass a `Timestamp` directly.
1546    /// One can also pass a `(Unit, Timestamp)`, where `Unit` is treated as
1547    /// [`TimestampDifference::largest`].
1548    ///
1549    /// # Properties
1550    ///
1551    /// It is guaranteed that if the returned span is subtracted from `other`,
1552    /// and if no rounding is requested, then the original timestamp will be
1553    /// returned.
1554    ///
1555    /// This routine is equivalent to `self.since(other).map(|span| -span)`
1556    /// if no rounding options are set. If rounding options are set, then
1557    /// it's equivalent to
1558    /// `self.since(other_without_rounding_options).map(|span| -span)`,
1559    /// followed by a call to [`Span::round`] with the appropriate rounding
1560    /// options set. This is because the negation of a span can result in
1561    /// different rounding results depending on the rounding mode.
1562    ///
1563    /// # Errors
1564    ///
1565    /// An error can occur in some cases when the requested configuration
1566    /// would result in a span that is beyond allowable limits. For example,
1567    /// the nanosecond component of a span cannot represent the span of
1568    /// time between the minimum and maximum timestamps supported by Jiff.
1569    /// Therefore, if one requests a span with its largest unit set to
1570    /// [`Unit::Nanosecond`], then it's possible for this routine to fail.
1571    ///
1572    /// An error can also occur if `TimestampDifference` is misconfigured. For
1573    /// example, if the smallest unit provided is bigger than the largest unit,
1574    /// or if the largest unit provided is bigger than hours. (To use bigger
1575    /// units with an instant in time, use [`Zoned::until`] instead.)
1576    ///
1577    /// It is guaranteed that if one provides a timestamp with the default
1578    /// [`TimestampDifference`] configuration, then this routine will never
1579    /// fail.
1580    ///
1581    /// # Example
1582    ///
1583    /// ```
1584    /// use jiff::{Timestamp, ToSpan};
1585    ///
1586    /// let earlier: Timestamp = "2006-08-24T22:30:00Z".parse()?;
1587    /// let later: Timestamp = "2019-01-31 21:00:00Z".parse()?;
1588    /// assert_eq!(earlier.until(later)?, 392509800.seconds().fieldwise());
1589    ///
1590    /// // Flipping the timestamps is fine, but you'll get a negative span.
1591    /// assert_eq!(later.until(earlier)?, -392509800.seconds().fieldwise());
1592    ///
1593    /// # Ok::<(), Box<dyn std::error::Error>>(())
1594    /// ```
1595    ///
1596    /// # Example: using bigger units
1597    ///
1598    /// This example shows how to expand the span returned to bigger units.
1599    /// This makes use of a `From<(Unit, Timestamp)> for TimestampDifference`
1600    /// trait implementation.
1601    ///
1602    /// ```
1603    /// use jiff::{Timestamp, ToSpan, Unit};
1604    ///
1605    /// let ts1: Timestamp = "1995-12-07T03:24:30.000003500Z".parse()?;
1606    /// let ts2: Timestamp = "2019-01-31 15:30:00Z".parse()?;
1607    ///
1608    /// // The default limits durations to using "seconds" as the biggest unit.
1609    /// let span = ts1.until(ts2)?;
1610    /// assert_eq!(span.to_string(), "PT730641929.9999965S");
1611    ///
1612    /// // But we can ask for units all the way up to hours.
1613    /// let span = ts1.until((Unit::Hour, ts2))?;
1614    /// assert_eq!(span.to_string(), "PT202956H5M29.9999965S");
1615    ///
1616    /// # Ok::<(), Box<dyn std::error::Error>>(())
1617    /// ```
1618    ///
1619    /// # Example: rounding the result
1620    ///
1621    /// This shows how one might find the difference between two timestamps and
1622    /// have the result rounded such that sub-seconds are removed.
1623    ///
1624    /// In this case, we need to hand-construct a [`TimestampDifference`]
1625    /// in order to gain full configurability.
1626    ///
1627    /// ```
1628    /// use jiff::{Timestamp, TimestampDifference, ToSpan, Unit};
1629    ///
1630    /// let ts1: Timestamp = "1995-12-07 03:24:30.000003500Z".parse()?;
1631    /// let ts2: Timestamp = "2019-01-31 15:30:00Z".parse()?;
1632    ///
1633    /// let span = ts1.until(
1634    ///     TimestampDifference::from(ts2).smallest(Unit::Second),
1635    /// )?;
1636    /// assert_eq!(span.to_string(), "PT730641929S");
1637    ///
1638    /// // We can combine smallest and largest units too!
1639    /// let span = ts1.until(
1640    ///     TimestampDifference::from(ts2)
1641    ///         .smallest(Unit::Second)
1642    ///         .largest(Unit::Hour),
1643    /// )?;
1644    /// assert_eq!(span.to_string(), "PT202956H5M29S");
1645    /// # Ok::<(), Box<dyn std::error::Error>>(())
1646    /// ```
1647    #[inline]
1648    pub fn until<A: Into<TimestampDifference>>(
1649        self,
1650        other: A,
1651    ) -> Result<Span, Error> {
1652        let args: TimestampDifference = other.into();
1653        let span = args.until_with_largest_unit(self)?;
1654        if args.rounding_may_change_span() {
1655            span.round(args.round)
1656        } else {
1657            Ok(span)
1658        }
1659    }
1660
1661    /// This routine is identical to [`Timestamp::until`], but the order of the
1662    /// parameters is flipped.
1663    ///
1664    /// # Errors
1665    ///
1666    /// This has the same error conditions as [`Timestamp::until`].
1667    ///
1668    /// # Example
1669    ///
1670    /// This routine can be used via the `-` operator. Since the default
1671    /// configuration is used and because a `Span` can represent the difference
1672    /// between any two possible timestamps, it will never panic.
1673    ///
1674    /// ```
1675    /// use jiff::{Timestamp, ToSpan};
1676    ///
1677    /// let earlier: Timestamp = "2006-08-24T22:30:00Z".parse()?;
1678    /// let later: Timestamp = "2019-01-31 21:00:00Z".parse()?;
1679    /// assert_eq!(later - earlier, 392509800.seconds().fieldwise());
1680    ///
1681    /// # Ok::<(), Box<dyn std::error::Error>>(())
1682    /// ```
1683    #[inline]
1684    pub fn since<A: Into<TimestampDifference>>(
1685        self,
1686        other: A,
1687    ) -> Result<Span, Error> {
1688        let args: TimestampDifference = other.into();
1689        let span = -args.until_with_largest_unit(self)?;
1690        if args.rounding_may_change_span() {
1691            span.round(args.round)
1692        } else {
1693            Ok(span)
1694        }
1695    }
1696
1697    /// Returns an absolute duration representing the elapsed time from this
1698    /// timestamp until the given `other` timestamp.
1699    ///
1700    /// When `other` occurs before this timestamp, then the duration returned
1701    /// will be negative.
1702    ///
1703    /// Unlike [`Timestamp::until`], this always returns a duration
1704    /// corresponding to a 96-bit integer of nanoseconds between two
1705    /// timestamps.
1706    ///
1707    /// # Fallibility
1708    ///
1709    /// This routine never panics or returns an error. Since there are no
1710    /// configuration options that can be incorrectly provided, no error is
1711    /// possible when calling this routine. In contrast, [`Timestamp::until`]
1712    /// can return an error in some cases due to misconfiguration. But like
1713    /// this routine, [`Timestamp::until`] never panics or returns an error in
1714    /// its default configuration.
1715    ///
1716    /// # When should I use this versus [`Timestamp::until`]?
1717    ///
1718    /// See the type documentation for [`SignedDuration`] for the section on
1719    /// when one should use [`Span`] and when one should use `SignedDuration`.
1720    /// In short, use `Span` (and therefore `Timestamp::until`) unless you have
1721    /// a specific reason to do otherwise.
1722    ///
1723    /// # Example
1724    ///
1725    /// ```
1726    /// use jiff::{Timestamp, SignedDuration};
1727    ///
1728    /// let earlier: Timestamp = "2006-08-24T22:30:00Z".parse()?;
1729    /// let later: Timestamp = "2019-01-31 21:00:00Z".parse()?;
1730    /// assert_eq!(
1731    ///     earlier.duration_until(later),
1732    ///     SignedDuration::from_secs(392509800),
1733    /// );
1734    ///
1735    /// // Flipping the timestamps is fine, but you'll get a negative span.
1736    /// assert_eq!(
1737    ///     later.duration_until(earlier),
1738    ///     SignedDuration::from_secs(-392509800),
1739    /// );
1740    ///
1741    /// # Ok::<(), Box<dyn std::error::Error>>(())
1742    /// ```
1743    ///
1744    /// # Example: difference with [`Timestamp::until`]
1745    ///
1746    /// The primary difference between this routine and
1747    /// `Timestamp::until`, other than the return type, is that this
1748    /// routine is likely to be faster. Namely, it does simple 96-bit
1749    /// integer math, where as `Timestamp::until` has to do a bit more
1750    /// work to deal with the different types of units on a `Span`.
1751    ///
1752    /// Additionally, since the difference between two timestamps is always
1753    /// expressed in units of hours or smaller, and units of hours or smaller
1754    /// are always uniform, there is no "expressive" difference between this
1755    /// routine and `Timestamp::until`. Because of this, one can always
1756    /// convert between `Span` and `SignedDuration` as returned by methods
1757    /// on `Timestamp` without a relative datetime:
1758    ///
1759    /// ```
1760    /// use jiff::{SignedDuration, Span, Timestamp};
1761    ///
1762    /// let ts1: Timestamp = "2024-02-28T00:00:00Z".parse()?;
1763    /// let ts2: Timestamp = "2024-03-01T00:00:00Z".parse()?;
1764    /// let dur = ts1.duration_until(ts2);
1765    /// // Guaranteed to never fail because the duration
1766    /// // between two civil times never exceeds the limits
1767    /// // of a `Span`.
1768    /// let span = Span::try_from(dur).unwrap();
1769    /// assert_eq!(format!("{span:#}"), "172800s");
1770    /// // Guaranteed to succeed and always return the original
1771    /// // duration because the units are always hours or smaller,
1772    /// // and thus uniform. This means a relative datetime is
1773    /// // never required to do this conversion.
1774    /// let dur = SignedDuration::try_from(span).unwrap();
1775    /// assert_eq!(dur, SignedDuration::from_secs(172_800));
1776    ///
1777    /// # Ok::<(), Box<dyn std::error::Error>>(())
1778    /// ```
1779    ///
1780    /// This conversion guarantee also applies to [`Timestamp::until`] since it
1781    /// always returns a balanced span. That is, it never returns spans like
1782    /// `1 second 1000 milliseconds`. (Those cannot be losslessly converted to
1783    /// a `SignedDuration` since a `SignedDuration` is only represented as a
1784    /// single 96-bit integer of nanoseconds.)
1785    #[inline]
1786    pub fn duration_until(self, other: Timestamp) -> SignedDuration {
1787        SignedDuration::timestamp_until(self, other)
1788    }
1789
1790    /// This routine is identical to [`Timestamp::duration_until`], but the
1791    /// order of the parameters is flipped.
1792    ///
1793    /// # Example
1794    ///
1795    /// ```
1796    /// use jiff::{SignedDuration, Timestamp};
1797    ///
1798    /// let earlier: Timestamp = "2006-08-24T22:30:00Z".parse()?;
1799    /// let later: Timestamp = "2019-01-31 21:00:00Z".parse()?;
1800    /// assert_eq!(
1801    ///     later.duration_since(earlier),
1802    ///     SignedDuration::from_secs(392509800),
1803    /// );
1804    ///
1805    /// # Ok::<(), Box<dyn std::error::Error>>(())
1806    /// ```
1807    #[inline]
1808    pub fn duration_since(self, other: Timestamp) -> SignedDuration {
1809        SignedDuration::timestamp_until(other, self)
1810    }
1811
1812    /// Rounds this timestamp according to the [`TimestampRound`] configuration
1813    /// given.
1814    ///
1815    /// The principal option is [`TimestampRound::smallest`], which allows
1816    /// one to configure the smallest units in the returned timestamp.
1817    /// Rounding is what determines whether the specified smallest unit
1818    /// should keep its current value or whether it should be incremented.
1819    /// Moreover, the amount it should be incremented can be configured via
1820    /// [`TimestampRound::increment`]. Finally, the rounding strategy itself
1821    /// can be configured via [`TimestampRound::mode`].
1822    ///
1823    /// Note that this routine is generic and accepts anything that
1824    /// implements `Into<TimestampRound>`. Some notable implementations are:
1825    ///
1826    /// * `From<Unit> for TimestampRound`, which will automatically create a
1827    /// `TimestampRound::new().smallest(unit)` from the unit provided.
1828    /// * `From<(Unit, i64)> for TimestampRound`, which will automatically
1829    /// create a `TimestampRound::new().smallest(unit).increment(number)` from
1830    /// the unit and increment provided.
1831    ///
1832    /// # Errors
1833    ///
1834    /// This returns an error if the smallest unit configured on the given
1835    /// [`TimestampRound`] is bigger than hours.
1836    ///
1837    /// The rounding increment, when combined with the smallest unit (which
1838    /// defaults to [`Unit::Nanosecond`]), must divide evenly into `86,400`
1839    /// seconds (one 24-hour civil day). For example, increments of both
1840    /// 45 seconds and 15 minutes are allowed, but 7 seconds and 25 minutes are
1841    /// both not allowed.
1842    ///
1843    /// # Example
1844    ///
1845    /// This is a basic example that demonstrates rounding a timestamp to the
1846    /// nearest hour. This also demonstrates calling this method with the
1847    /// smallest unit directly, instead of constructing a `TimestampRound`
1848    /// manually.
1849    ///
1850    /// ```
1851    /// use jiff::{Timestamp, Unit};
1852    ///
1853    /// let ts: Timestamp = "2024-06-19 15:30:00Z".parse()?;
1854    /// assert_eq!(
1855    ///     ts.round(Unit::Hour)?.to_string(),
1856    ///     "2024-06-19T16:00:00Z",
1857    /// );
1858    /// let ts: Timestamp = "2024-06-19 15:29:59Z".parse()?;
1859    /// assert_eq!(
1860    ///     ts.round(Unit::Hour)?.to_string(),
1861    ///     "2024-06-19T15:00:00Z",
1862    /// );
1863    ///
1864    /// # Ok::<(), Box<dyn std::error::Error>>(())
1865    /// ```
1866    ///
1867    /// # Example: changing the rounding mode
1868    ///
1869    /// The default rounding mode is [`RoundMode::HalfExpand`], which
1870    /// breaks ties by rounding away from zero. But other modes like
1871    /// [`RoundMode::Trunc`] can be used too:
1872    ///
1873    /// ```
1874    /// use jiff::{RoundMode, Timestamp, TimestampRound, Unit};
1875    ///
1876    /// // The default will round up to the next hour for any time past the
1877    /// // 30 minute mark, but using truncation rounding will always round
1878    /// // down.
1879    /// let ts: Timestamp = "2024-06-19 15:30:00Z".parse()?;
1880    /// assert_eq!(
1881    ///     ts.round(
1882    ///         TimestampRound::new()
1883    ///             .smallest(Unit::Hour)
1884    ///             .mode(RoundMode::Trunc),
1885    ///     )?.to_string(),
1886    ///     "2024-06-19T15:00:00Z",
1887    /// );
1888    ///
1889    /// # Ok::<(), Box<dyn std::error::Error>>(())
1890    /// ```
1891    ///
1892    /// # Example: rounding to the nearest 5 minute increment
1893    ///
1894    /// ```
1895    /// use jiff::{Timestamp, Unit};
1896    ///
1897    /// // rounds down
1898    /// let ts: Timestamp = "2024-06-19T15:27:29.999999999Z".parse()?;
1899    /// assert_eq!(
1900    ///     ts.round((Unit::Minute, 5))?.to_string(),
1901    ///     "2024-06-19T15:25:00Z",
1902    /// );
1903    /// // rounds up
1904    /// let ts: Timestamp = "2024-06-19T15:27:30Z".parse()?;
1905    /// assert_eq!(
1906    ///     ts.round((Unit::Minute, 5))?.to_string(),
1907    ///     "2024-06-19T15:30:00Z",
1908    /// );
1909    ///
1910    /// # Ok::<(), Box<dyn std::error::Error>>(())
1911    /// ```
1912    #[inline]
1913    pub fn round<R: Into<TimestampRound>>(
1914        self,
1915        options: R,
1916    ) -> Result<Timestamp, Error> {
1917        let options: TimestampRound = options.into();
1918        options.round(self)
1919    }
1920
1921    /// Return an iterator of periodic timestamps determined by the given span.
1922    ///
1923    /// The given span may be negative, in which case, the iterator will move
1924    /// backwards through time. The iterator won't stop until either the span
1925    /// itself overflows, or it would otherwise exceed the minimum or maximum
1926    /// `Timestamp` value.
1927    ///
1928    /// # Example: when to check a glucose monitor
1929    ///
1930    /// When my cat had diabetes, my veterinarian installed a glucose monitor
1931    /// and instructed me to scan it about every 5 hours. This example lists
1932    /// all of the times I need to scan it for the 2 days following its
1933    /// installation:
1934    ///
1935    /// ```
1936    /// use jiff::{Timestamp, ToSpan};
1937    ///
1938    /// let start: Timestamp = "2023-07-15 16:30:00-04".parse()?;
1939    /// let end = start.checked_add(48.hours())?;
1940    /// let mut scan_times = vec![];
1941    /// for ts in start.series(5.hours()).take_while(|&ts| ts <= end) {
1942    ///     scan_times.push(ts);
1943    /// }
1944    /// assert_eq!(scan_times, vec![
1945    ///     "2023-07-15 16:30:00-04:00".parse::<Timestamp>()?,
1946    ///     "2023-07-15 21:30:00-04:00".parse::<Timestamp>()?,
1947    ///     "2023-07-16 02:30:00-04:00".parse::<Timestamp>()?,
1948    ///     "2023-07-16 07:30:00-04:00".parse::<Timestamp>()?,
1949    ///     "2023-07-16 12:30:00-04:00".parse::<Timestamp>()?,
1950    ///     "2023-07-16 17:30:00-04:00".parse::<Timestamp>()?,
1951    ///     "2023-07-16 22:30:00-04:00".parse::<Timestamp>()?,
1952    ///     "2023-07-17 03:30:00-04:00".parse::<Timestamp>()?,
1953    ///     "2023-07-17 08:30:00-04:00".parse::<Timestamp>()?,
1954    ///     "2023-07-17 13:30:00-04:00".parse::<Timestamp>()?,
1955    /// ]);
1956    ///
1957    /// # Ok::<(), Box<dyn std::error::Error>>(())
1958    /// ```
1959    #[inline]
1960    pub fn series(self, period: Span) -> TimestampSeries {
1961        TimestampSeries::new(self, period)
1962    }
1963}
1964
1965/// Parsing and formatting APIs.
1966impl Timestamp {
1967    /// Parses a timestamp (expressed as broken down time) in `input` matching
1968    /// the given `format`.
1969    ///
1970    /// The format string uses a "printf"-style API where conversion
1971    /// specifiers can be used as place holders to match components of
1972    /// a datetime. For details on the specifiers supported, see the
1973    /// [`fmt::strtime`] module documentation.
1974    ///
1975    /// # Errors
1976    ///
1977    /// This returns an error when parsing failed. This might happen because
1978    /// the format string itself was invalid, or because the input didn't match
1979    /// the format string.
1980    ///
1981    /// This also returns an error if there wasn't sufficient information to
1982    /// construct a timestamp. For example, if an offset wasn't parsed. (The
1983    /// offset is needed to turn the civil time parsed into a precise instant
1984    /// in time.)
1985    ///
1986    /// # Example
1987    ///
1988    /// This example shows how to parse a datetime string into a timestamp:
1989    ///
1990    /// ```
1991    /// use jiff::Timestamp;
1992    ///
1993    /// let ts = Timestamp::strptime("%F %H:%M %:z", "2024-07-14 21:14 -04:00")?;
1994    /// assert_eq!(ts.to_string(), "2024-07-15T01:14:00Z");
1995    ///
1996    /// # Ok::<(), Box<dyn std::error::Error>>(())
1997    /// ```
1998    #[inline]
1999    pub fn strptime(
2000        format: impl AsRef<[u8]>,
2001        input: impl AsRef<[u8]>,
2002    ) -> Result<Timestamp, Error> {
2003        fmt::strtime::parse(format, input).and_then(|tm| tm.to_timestamp())
2004    }
2005
2006    /// Formats this timestamp according to the given `format`.
2007    ///
2008    /// The format string uses a "printf"-style API where conversion
2009    /// specifiers can be used as place holders to format components of
2010    /// a datetime. For details on the specifiers supported, see the
2011    /// [`fmt::strtime`] module documentation.
2012    ///
2013    /// # Errors and panics
2014    ///
2015    /// While this routine itself does not error or panic, using the value
2016    /// returned may result in a panic if formatting fails. See the
2017    /// documentation on [`fmt::strtime::Display`] for more information.
2018    ///
2019    /// To format in a way that surfaces errors without panicking, use either
2020    /// [`fmt::strtime::format`] or [`fmt::strtime::BrokenDownTime::format`].
2021    ///
2022    /// # Example
2023    ///
2024    /// This shows how to format a timestamp into a human readable datetime
2025    /// in UTC:
2026    ///
2027    /// ```
2028    /// use jiff::{civil::date, Timestamp};
2029    ///
2030    /// let ts = Timestamp::from_second(86_400)?;
2031    /// let string = ts.strftime("%a %b %e %I:%M:%S %p UTC %Y").to_string();
2032    /// assert_eq!(string, "Fri Jan  2 12:00:00 AM UTC 1970");
2033    ///
2034    /// # Ok::<(), Box<dyn std::error::Error>>(())
2035    /// ```
2036    #[inline]
2037    pub fn strftime<'f, F: 'f + ?Sized + AsRef<[u8]>>(
2038        &self,
2039        format: &'f F,
2040    ) -> fmt::strtime::Display<'f> {
2041        fmt::strtime::Display { fmt: format.as_ref(), tm: (*self).into() }
2042    }
2043
2044    /// Format a `Timestamp` datetime into a string with the given offset.
2045    ///
2046    /// This will format to an RFC 3339 compatible string with an offset.
2047    ///
2048    /// This will never use either `Z` (for Zulu time) or `-00:00` as an
2049    /// offset. This is because Zulu time (and `-00:00`) mean "the time in UTC
2050    /// is known, but the offset to local time is unknown." Since this routine
2051    /// accepts an explicit offset, the offset is known. For example,
2052    /// `Offset::UTC` will be formatted as `+00:00`.
2053    ///
2054    /// To format an RFC 3339 string in Zulu time, use the default
2055    /// [`std::fmt::Display`] trait implementation on `Timestamp`.
2056    ///
2057    /// # Example
2058    ///
2059    /// ```
2060    /// use jiff::{tz, Timestamp};
2061    ///
2062    /// let ts = Timestamp::from_second(1)?;
2063    /// assert_eq!(
2064    ///     ts.display_with_offset(tz::offset(-5)).to_string(),
2065    ///     "1969-12-31T19:00:01-05:00",
2066    /// );
2067    ///
2068    /// # Ok::<(), Box<dyn std::error::Error>>(())
2069    /// ```
2070    #[inline]
2071    pub fn display_with_offset(
2072        &self,
2073        offset: Offset,
2074    ) -> TimestampDisplayWithOffset {
2075        TimestampDisplayWithOffset { timestamp: *self, offset }
2076    }
2077}
2078
2079/// Internal APIs using Jiff ranged integers.
2080impl Timestamp {
2081    #[inline]
2082    pub(crate) fn new_ranged(
2083        second: impl RInto<UnixSeconds>,
2084        nanosecond: impl RInto<FractionalNanosecond>,
2085    ) -> Result<Timestamp, Error> {
2086        let (second, nanosecond) = (second.rinto(), nanosecond.rinto());
2087        if second == UnixSeconds::MIN_REPR && nanosecond < 0 {
2088            return Err(Error::range(
2089                "seconds and nanoseconds",
2090                nanosecond,
2091                0,
2092                1_000_000_000,
2093            ));
2094        }
2095        // We now normalize our seconds and nanoseconds such that they have
2096        // the same sign (or where one is zero). So for example, when given
2097        // `-1s 1ns`, then we should turn that into `-999,999,999ns`.
2098        //
2099        // But first, if we're already normalized, we're done!
2100        if second.signum() == nanosecond.signum()
2101            || second == 0
2102            || nanosecond == 0
2103        {
2104            return Ok(Timestamp { second, nanosecond });
2105        }
2106        let second = second.without_bounds();
2107        let nanosecond = nanosecond.without_bounds();
2108        let [delta_second, delta_nanosecond] = t::NoUnits::vary_many(
2109            [second, nanosecond],
2110            |[second, nanosecond]| {
2111                if second < 0 && nanosecond > 0 {
2112                    [C(1), (-t::NANOS_PER_SECOND).rinto()]
2113                } else if second > 0 && nanosecond < 0 {
2114                    [C(-1), t::NANOS_PER_SECOND.rinto()]
2115                } else {
2116                    [C(0), C(0)]
2117                }
2118            },
2119        );
2120        Ok(Timestamp {
2121            second: (second + delta_second).rinto(),
2122            nanosecond: (nanosecond + delta_nanosecond).rinto(),
2123        })
2124    }
2125
2126    #[inline]
2127    pub(crate) fn new_ranged_unchecked(
2128        second: UnixSeconds,
2129        nanosecond: FractionalNanosecond,
2130    ) -> Timestamp {
2131        Timestamp { second, nanosecond }
2132    }
2133
2134    #[inline]
2135    fn from_second_ranged(second: UnixSeconds) -> Timestamp {
2136        Timestamp { second, nanosecond: FractionalNanosecond::N::<0>() }
2137    }
2138
2139    #[inline]
2140    fn from_millisecond_ranged(millisecond: UnixMilliseconds) -> Timestamp {
2141        let second =
2142            UnixSeconds::rfrom(millisecond.div_ceil(t::MILLIS_PER_SECOND));
2143        let nanosecond = FractionalNanosecond::rfrom(
2144            millisecond.rem_ceil(t::MILLIS_PER_SECOND) * t::NANOS_PER_MILLI,
2145        );
2146        Timestamp { second, nanosecond }
2147    }
2148
2149    #[inline]
2150    fn from_microsecond_ranged(microsecond: UnixMicroseconds) -> Timestamp {
2151        let second =
2152            UnixSeconds::rfrom(microsecond.div_ceil(t::MICROS_PER_SECOND));
2153        let nanosecond = FractionalNanosecond::rfrom(
2154            microsecond.rem_ceil(t::MICROS_PER_SECOND) * t::NANOS_PER_MICRO,
2155        );
2156        Timestamp { second, nanosecond }
2157    }
2158
2159    #[inline]
2160    pub(crate) fn from_nanosecond_ranged(
2161        nanosecond: UnixNanoseconds,
2162    ) -> Timestamp {
2163        let second =
2164            UnixSeconds::rfrom(nanosecond.div_ceil(t::NANOS_PER_SECOND));
2165        let nanosecond = nanosecond.rem_ceil(t::NANOS_PER_SECOND).rinto();
2166        Timestamp { second, nanosecond }
2167    }
2168
2169    #[inline]
2170    pub(crate) fn as_second_ranged(self) -> UnixSeconds {
2171        self.second
2172    }
2173
2174    #[inline]
2175    fn as_millisecond_ranged(self) -> UnixMilliseconds {
2176        let second = NoUnits::rfrom(self.as_second_ranged());
2177        let nanosecond = NoUnits::rfrom(self.subsec_nanosecond_ranged());
2178        // The minimum value of a timestamp has the fractional nanosecond set
2179        // to 0, but otherwise its minimum value is -999_999_999. So to avoid
2180        // a case where we return a ranged integer with a minimum value less
2181        // than the actual minimum, we clamp the fractional part to 0 when the
2182        // second value is the minimum.
2183        let [second, nanosecond] =
2184            NoUnits::vary_many([second, nanosecond], |[second, nanosecond]| {
2185                if second == UnixSeconds::MIN_REPR && nanosecond < 0 {
2186                    [second, C(0).rinto()]
2187                } else {
2188                    [second, nanosecond]
2189                }
2190            });
2191        UnixMilliseconds::rfrom(
2192            (second * t::MILLIS_PER_SECOND)
2193                + (nanosecond.div_ceil(t::NANOS_PER_MILLI)),
2194        )
2195    }
2196
2197    #[inline]
2198    fn as_microsecond_ranged(self) -> UnixMicroseconds {
2199        let second = NoUnits::rfrom(self.as_second_ranged());
2200        let nanosecond = NoUnits::rfrom(self.subsec_nanosecond_ranged());
2201        // The minimum value of a timestamp has the fractional nanosecond set
2202        // to 0, but otherwise its minimum value is -999_999_999. So to avoid
2203        // a case where we return a ranged integer with a minimum value less
2204        // than the actual minimum, we clamp the fractional part to 0 when the
2205        // second value is the minimum.
2206        let [second, nanosecond] =
2207            NoUnits::vary_many([second, nanosecond], |[second, nanosecond]| {
2208                if second == UnixSeconds::MIN_REPR && nanosecond < 0 {
2209                    [second, C(0).rinto()]
2210                } else {
2211                    [second, nanosecond]
2212                }
2213            });
2214        UnixMicroseconds::rfrom(
2215            (second * t::MICROS_PER_SECOND)
2216                + (nanosecond.div_ceil(t::NANOS_PER_MICRO)),
2217        )
2218    }
2219
2220    #[inline]
2221    pub(crate) fn as_nanosecond_ranged(self) -> UnixNanoseconds {
2222        let second = NoUnits128::rfrom(self.as_second_ranged());
2223        let nanosecond = NoUnits128::rfrom(self.subsec_nanosecond_ranged());
2224        // The minimum value of a timestamp has the fractional nanosecond set
2225        // to 0, but otherwise its minimum value is -999_999_999. So to avoid
2226        // a case where we return a ranged integer with a minimum value less
2227        // than the actual minimum, we clamp the fractional part to 0 when the
2228        // second value is the minimum.
2229        let [second, nanosecond] = NoUnits128::vary_many(
2230            [second, nanosecond],
2231            |[second, nanosecond]| {
2232                if second == UnixSeconds::MIN_REPR && nanosecond < 0 {
2233                    [second, C(0).rinto()]
2234                } else {
2235                    [second, nanosecond]
2236                }
2237            },
2238        );
2239        UnixNanoseconds::rfrom(second * t::NANOS_PER_SECOND + nanosecond)
2240    }
2241
2242    #[inline]
2243    fn subsec_millisecond_ranged(self) -> t::FractionalMillisecond {
2244        let millis =
2245            self.subsec_nanosecond_ranged().div_ceil(t::NANOS_PER_MILLI);
2246        t::FractionalMillisecond::rfrom(millis)
2247    }
2248
2249    #[inline]
2250    fn subsec_microsecond_ranged(self) -> t::FractionalMicrosecond {
2251        let micros =
2252            self.subsec_nanosecond_ranged().div_ceil(t::NANOS_PER_MICRO);
2253        t::FractionalMicrosecond::rfrom(micros)
2254    }
2255
2256    #[inline]
2257    pub(crate) fn subsec_nanosecond_ranged(self) -> FractionalNanosecond {
2258        self.nanosecond
2259    }
2260}
2261
2262impl Default for Timestamp {
2263    #[inline]
2264    fn default() -> Timestamp {
2265        Timestamp::UNIX_EPOCH
2266    }
2267}
2268
2269/// Converts a `Timestamp` datetime into a human readable datetime string.
2270///
2271/// (This `Debug` representation currently emits the same string as the
2272/// `Display` representation, but this is not a guarantee.)
2273///
2274/// Options currently supported:
2275///
2276/// * [`std::fmt::Formatter::precision`] can be set to control the precision
2277/// of the fractional second component.
2278///
2279/// # Example
2280///
2281/// ```
2282/// use jiff::Timestamp;
2283///
2284/// let ts = Timestamp::new(1_123_456_789, 123_000_000)?;
2285/// assert_eq!(
2286///     format!("{ts:.6?}"),
2287///     "2005-08-07T23:19:49.123000Z",
2288/// );
2289/// // Precision values greater than 9 are clamped to 9.
2290/// assert_eq!(
2291///     format!("{ts:.300?}"),
2292///     "2005-08-07T23:19:49.123000000Z",
2293/// );
2294/// // A precision of 0 implies the entire fractional
2295/// // component is always truncated.
2296/// assert_eq!(
2297///     format!("{ts:.0?}"),
2298///     "2005-08-07T23:19:49Z",
2299/// );
2300///
2301/// # Ok::<(), Box<dyn std::error::Error>>(())
2302/// ```
2303impl core::fmt::Debug for Timestamp {
2304    #[inline]
2305    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
2306        core::fmt::Display::fmt(self, f)
2307    }
2308}
2309
2310/// Converts a `Timestamp` datetime into a RFC 3339 compliant string.
2311///
2312/// Since a `Timestamp` never has an offset associated with it and is always
2313/// in UTC, the string emitted by this trait implementation uses `Z` for "Zulu"
2314/// time. The significance of Zulu time is prescribed by RFC 9557 and means
2315/// that "the time in UTC is known, but the offset to local time is unknown."
2316/// If you need to emit an RFC 3339 compliant string with a specific offset,
2317/// then use [`Timestamp::display_with_offset`].
2318///
2319/// # Forrmatting options supported
2320///
2321/// * [`std::fmt::Formatter::precision`] can be set to control the precision
2322/// of the fractional second component.
2323///
2324/// # Example
2325///
2326/// ```
2327/// use jiff::Timestamp;
2328///
2329/// let ts = Timestamp::new(1_123_456_789, 123_000_000)?;
2330/// assert_eq!(
2331///     format!("{ts:.6}"),
2332///     "2005-08-07T23:19:49.123000Z",
2333/// );
2334/// // Precision values greater than 9 are clamped to 9.
2335/// assert_eq!(
2336///     format!("{ts:.300}"),
2337///     "2005-08-07T23:19:49.123000000Z",
2338/// );
2339/// // A precision of 0 implies the entire fractional
2340/// // component is always truncated.
2341/// assert_eq!(
2342///     format!("{ts:.0}"),
2343///     "2005-08-07T23:19:49Z",
2344/// );
2345///
2346/// # Ok::<(), Box<dyn std::error::Error>>(())
2347/// ```
2348impl core::fmt::Display for Timestamp {
2349    #[inline]
2350    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
2351        use crate::fmt::StdFmtWrite;
2352
2353        let precision =
2354            f.precision().map(|p| u8::try_from(p).unwrap_or(u8::MAX));
2355        temporal::DateTimePrinter::new()
2356            .precision(precision)
2357            .print_timestamp(self, StdFmtWrite(f))
2358            .map_err(|_| core::fmt::Error)
2359    }
2360}
2361
2362impl core::str::FromStr for Timestamp {
2363    type Err = Error;
2364
2365    #[inline]
2366    fn from_str(string: &str) -> Result<Timestamp, Error> {
2367        DEFAULT_DATETIME_PARSER.parse_timestamp(string)
2368    }
2369}
2370
2371impl Eq for Timestamp {}
2372
2373impl PartialEq for Timestamp {
2374    #[inline]
2375    fn eq(&self, rhs: &Timestamp) -> bool {
2376        self.as_second_ranged().get() == rhs.as_second_ranged().get()
2377            && self.subsec_nanosecond_ranged().get()
2378                == rhs.subsec_nanosecond_ranged().get()
2379    }
2380}
2381
2382impl Ord for Timestamp {
2383    #[inline]
2384    fn cmp(&self, rhs: &Timestamp) -> core::cmp::Ordering {
2385        (self.as_second_ranged().get(), self.subsec_nanosecond_ranged().get())
2386            .cmp(&(
2387                rhs.as_second_ranged().get(),
2388                rhs.subsec_nanosecond_ranged().get(),
2389            ))
2390    }
2391}
2392
2393impl PartialOrd for Timestamp {
2394    #[inline]
2395    fn partial_cmp(&self, rhs: &Timestamp) -> Option<core::cmp::Ordering> {
2396        Some(self.cmp(rhs))
2397    }
2398}
2399
2400impl core::hash::Hash for Timestamp {
2401    #[inline]
2402    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
2403        self.as_second_ranged().get().hash(state);
2404        self.subsec_nanosecond_ranged().get().hash(state);
2405    }
2406}
2407
2408/// Adds a span of time to a timestamp.
2409///
2410/// This uses checked arithmetic and panics when it fails. To handle arithmetic
2411/// without panics, use [`Timestamp::checked_add`]. Note that the failure
2412/// condition includes overflow and using a `Span` with non-zero units greater
2413/// than hours.
2414impl core::ops::Add<Span> for Timestamp {
2415    type Output = Timestamp;
2416
2417    #[inline]
2418    fn add(self, rhs: Span) -> Timestamp {
2419        self.checked_add_span(rhs).expect("adding span to timestamp failed")
2420    }
2421}
2422
2423/// Adds a span of time to a timestamp in place.
2424///
2425/// This uses checked arithmetic and panics when it fails. To handle arithmetic
2426/// without panics, use [`Timestamp::checked_add`]. Note that the failure
2427/// condition includes overflow and using a `Span` with non-zero units greater
2428/// than hours.
2429impl core::ops::AddAssign<Span> for Timestamp {
2430    #[inline]
2431    fn add_assign(&mut self, rhs: Span) {
2432        *self = *self + rhs
2433    }
2434}
2435
2436/// Subtracts a span of time from a timestamp.
2437///
2438/// This uses checked arithmetic and panics when it fails. To handle arithmetic
2439/// without panics, use [`Timestamp::checked_sub`]. Note that the failure
2440/// condition includes overflow and using a `Span` with non-zero units greater
2441/// than hours.
2442impl core::ops::Sub<Span> for Timestamp {
2443    type Output = Timestamp;
2444
2445    #[inline]
2446    fn sub(self, rhs: Span) -> Timestamp {
2447        self.checked_add_span(rhs.negate())
2448            .expect("subtracting span from timestamp failed")
2449    }
2450}
2451
2452/// Subtracts a span of time from a timestamp in place.
2453///
2454/// This uses checked arithmetic and panics when it fails. To handle arithmetic
2455/// without panics, use [`Timestamp::checked_sub`]. Note that the failure
2456/// condition includes overflow and using a `Span` with non-zero units greater
2457/// than hours.
2458impl core::ops::SubAssign<Span> for Timestamp {
2459    #[inline]
2460    fn sub_assign(&mut self, rhs: Span) {
2461        *self = *self - rhs
2462    }
2463}
2464
2465/// Computes the span of time between two timestamps.
2466///
2467/// This will return a negative span when the timestamp being subtracted is
2468/// greater.
2469///
2470/// Since this uses the default configuration for calculating a span between
2471/// two timestamps (no rounding and largest units is seconds), this will never
2472/// panic or fail in any way.
2473///
2474/// To configure the largest unit or enable rounding, use [`Timestamp::since`].
2475impl core::ops::Sub for Timestamp {
2476    type Output = Span;
2477
2478    #[inline]
2479    fn sub(self, rhs: Timestamp) -> Span {
2480        self.since(rhs).expect("since never fails when given Timestamp")
2481    }
2482}
2483
2484/// Adds a signed duration of time to a timestamp.
2485///
2486/// This uses checked arithmetic and panics on overflow. To handle overflow
2487/// without panics, use [`Timestamp::checked_add`].
2488impl core::ops::Add<SignedDuration> for Timestamp {
2489    type Output = Timestamp;
2490
2491    #[inline]
2492    fn add(self, rhs: SignedDuration) -> Timestamp {
2493        self.checked_add_duration(rhs)
2494            .expect("adding signed duration to timestamp overflowed")
2495    }
2496}
2497
2498/// Adds a signed duration of time to a timestamp in place.
2499///
2500/// This uses checked arithmetic and panics on overflow. To handle overflow
2501/// without panics, use [`Timestamp::checked_add`].
2502impl core::ops::AddAssign<SignedDuration> for Timestamp {
2503    #[inline]
2504    fn add_assign(&mut self, rhs: SignedDuration) {
2505        *self = *self + rhs
2506    }
2507}
2508
2509/// Subtracts a signed duration of time from a timestamp.
2510///
2511/// This uses checked arithmetic and panics on overflow. To handle overflow
2512/// without panics, use [`Timestamp::checked_sub`].
2513impl core::ops::Sub<SignedDuration> for Timestamp {
2514    type Output = Timestamp;
2515
2516    #[inline]
2517    fn sub(self, rhs: SignedDuration) -> Timestamp {
2518        let rhs = rhs
2519            .checked_neg()
2520            .expect("signed duration negation resulted in overflow");
2521        self.checked_add_duration(rhs)
2522            .expect("subtracting signed duration from timestamp overflowed")
2523    }
2524}
2525
2526/// Subtracts a signed duration of time from a timestamp in place.
2527///
2528/// This uses checked arithmetic and panics on overflow. To handle overflow
2529/// without panics, use [`Timestamp::checked_sub`].
2530impl core::ops::SubAssign<SignedDuration> for Timestamp {
2531    #[inline]
2532    fn sub_assign(&mut self, rhs: SignedDuration) {
2533        *self = *self - rhs
2534    }
2535}
2536
2537/// Adds an unsigned duration of time to a timestamp.
2538///
2539/// This uses checked arithmetic and panics on overflow. To handle overflow
2540/// without panics, use [`Timestamp::checked_add`].
2541impl core::ops::Add<UnsignedDuration> for Timestamp {
2542    type Output = Timestamp;
2543
2544    #[inline]
2545    fn add(self, rhs: UnsignedDuration) -> Timestamp {
2546        self.checked_add(rhs)
2547            .expect("adding unsigned duration to timestamp overflowed")
2548    }
2549}
2550
2551/// Adds an unsigned duration of time to a timestamp in place.
2552///
2553/// This uses checked arithmetic and panics on overflow. To handle overflow
2554/// without panics, use [`Timestamp::checked_add`].
2555impl core::ops::AddAssign<UnsignedDuration> for Timestamp {
2556    #[inline]
2557    fn add_assign(&mut self, rhs: UnsignedDuration) {
2558        *self = *self + rhs
2559    }
2560}
2561
2562/// Subtracts an unsigned duration of time from a timestamp.
2563///
2564/// This uses checked arithmetic and panics on overflow. To handle overflow
2565/// without panics, use [`Timestamp::checked_sub`].
2566impl core::ops::Sub<UnsignedDuration> for Timestamp {
2567    type Output = Timestamp;
2568
2569    #[inline]
2570    fn sub(self, rhs: UnsignedDuration) -> Timestamp {
2571        self.checked_sub(rhs)
2572            .expect("subtracting unsigned duration from timestamp overflowed")
2573    }
2574}
2575
2576/// Subtracts an unsigned duration of time from a timestamp in place.
2577///
2578/// This uses checked arithmetic and panics on overflow. To handle overflow
2579/// without panics, use [`Timestamp::checked_sub`].
2580impl core::ops::SubAssign<UnsignedDuration> for Timestamp {
2581    #[inline]
2582    fn sub_assign(&mut self, rhs: UnsignedDuration) {
2583        *self = *self - rhs
2584    }
2585}
2586
2587impl From<Zoned> for Timestamp {
2588    #[inline]
2589    fn from(zdt: Zoned) -> Timestamp {
2590        zdt.timestamp()
2591    }
2592}
2593
2594impl<'a> From<&'a Zoned> for Timestamp {
2595    #[inline]
2596    fn from(zdt: &'a Zoned) -> Timestamp {
2597        zdt.timestamp()
2598    }
2599}
2600
2601#[cfg(feature = "std")]
2602impl From<Timestamp> for std::time::SystemTime {
2603    #[inline]
2604    fn from(time: Timestamp) -> std::time::SystemTime {
2605        let unix_epoch = std::time::SystemTime::UNIX_EPOCH;
2606        let sdur = time.as_duration();
2607        let dur = sdur.unsigned_abs();
2608        // These are guaranteed to succeed because we assume that SystemTime
2609        // uses at least 64 bits for the time, and our durations are capped via
2610        // the range on UnixSeconds.
2611        if sdur.is_negative() {
2612            unix_epoch.checked_sub(dur).expect("duration too big (negative)")
2613        } else {
2614            unix_epoch.checked_add(dur).expect("duration too big (positive)")
2615        }
2616    }
2617}
2618
2619#[cfg(feature = "std")]
2620impl TryFrom<std::time::SystemTime> for Timestamp {
2621    type Error = Error;
2622
2623    #[inline]
2624    fn try_from(
2625        system_time: std::time::SystemTime,
2626    ) -> Result<Timestamp, Error> {
2627        let unix_epoch = std::time::SystemTime::UNIX_EPOCH;
2628        let dur = SignedDuration::system_until(unix_epoch, system_time)?;
2629        Timestamp::from_duration(dur)
2630    }
2631}
2632
2633#[cfg(feature = "serde")]
2634impl serde::Serialize for Timestamp {
2635    #[inline]
2636    fn serialize<S: serde::Serializer>(
2637        &self,
2638        serializer: S,
2639    ) -> Result<S::Ok, S::Error> {
2640        serializer.collect_str(self)
2641    }
2642}
2643
2644#[cfg(feature = "serde")]
2645impl<'de> serde::Deserialize<'de> for Timestamp {
2646    #[inline]
2647    fn deserialize<D: serde::Deserializer<'de>>(
2648        deserializer: D,
2649    ) -> Result<Timestamp, D::Error> {
2650        use serde::de;
2651
2652        struct TimestampVisitor;
2653
2654        impl<'de> de::Visitor<'de> for TimestampVisitor {
2655            type Value = Timestamp;
2656
2657            fn expecting(
2658                &self,
2659                f: &mut core::fmt::Formatter,
2660            ) -> core::fmt::Result {
2661                f.write_str("a timestamp string")
2662            }
2663
2664            #[inline]
2665            fn visit_bytes<E: de::Error>(
2666                self,
2667                value: &[u8],
2668            ) -> Result<Timestamp, E> {
2669                DEFAULT_DATETIME_PARSER
2670                    .parse_timestamp(value)
2671                    .map_err(de::Error::custom)
2672            }
2673
2674            #[inline]
2675            fn visit_str<E: de::Error>(
2676                self,
2677                value: &str,
2678            ) -> Result<Timestamp, E> {
2679                self.visit_bytes(value.as_bytes())
2680            }
2681        }
2682
2683        deserializer.deserialize_str(TimestampVisitor)
2684    }
2685}
2686
2687#[cfg(test)]
2688impl quickcheck::Arbitrary for Timestamp {
2689    fn arbitrary(g: &mut quickcheck::Gen) -> Timestamp {
2690        use quickcheck::Arbitrary;
2691
2692        let seconds: UnixSeconds = Arbitrary::arbitrary(g);
2693        let mut nanoseconds: FractionalNanosecond = Arbitrary::arbitrary(g);
2694        // nanoseconds must be zero for the minimum second value,
2695        // so just clamp it to 0.
2696        if seconds == UnixSeconds::MIN_REPR && nanoseconds < 0 {
2697            nanoseconds = C(0).rinto();
2698        }
2699        Timestamp::new_ranged(seconds, nanoseconds).unwrap_or_default()
2700    }
2701
2702    fn shrink(&self) -> alloc::boxed::Box<dyn Iterator<Item = Self>> {
2703        let second = self.as_second_ranged();
2704        let nanosecond = self.subsec_nanosecond_ranged();
2705        alloc::boxed::Box::new((second, nanosecond).shrink().filter_map(
2706            |(second, nanosecond)| {
2707                if second == UnixSeconds::MIN_REPR && nanosecond > 0 {
2708                    None
2709                } else {
2710                    Timestamp::new_ranged(second, nanosecond).ok()
2711                }
2712            },
2713        ))
2714    }
2715}
2716
2717/// A type for formatting a [`Timestamp`] with a specific offset.
2718///
2719/// This type is created by the [`Timestamp::display_with_offset`] method.
2720///
2721/// Like the [`std::fmt::Display`] trait implementation for `Timestamp`, this
2722/// always emits an RFC 3339 compliant string. Unlike `Timestamp`'s `Display`
2723/// trait implementation, which always uses `Z` or "Zulu" time, this always
2724/// uses an offfset.
2725///
2726/// # Forrmatting options supported
2727///
2728/// * [`std::fmt::Formatter::precision`] can be set to control the precision
2729/// of the fractional second component.
2730///
2731/// # Example
2732///
2733/// ```
2734/// use jiff::{tz, Timestamp};
2735///
2736/// let offset = tz::offset(-5);
2737/// let ts = Timestamp::new(1_123_456_789, 123_000_000)?;
2738/// assert_eq!(
2739///     format!("{ts:.6}", ts = ts.display_with_offset(offset)),
2740///     "2005-08-07T18:19:49.123000-05:00",
2741/// );
2742/// // Precision values greater than 9 are clamped to 9.
2743/// assert_eq!(
2744///     format!("{ts:.300}", ts = ts.display_with_offset(offset)),
2745///     "2005-08-07T18:19:49.123000000-05:00",
2746/// );
2747/// // A precision of 0 implies the entire fractional
2748/// // component is always truncated.
2749/// assert_eq!(
2750///     format!("{ts:.0}", ts = ts.display_with_offset(tz::Offset::UTC)),
2751///     "2005-08-07T23:19:49+00:00",
2752/// );
2753///
2754/// # Ok::<(), Box<dyn std::error::Error>>(())
2755/// ```
2756#[derive(Clone, Copy, Debug)]
2757pub struct TimestampDisplayWithOffset {
2758    timestamp: Timestamp,
2759    offset: Offset,
2760}
2761
2762impl core::fmt::Display for TimestampDisplayWithOffset {
2763    #[inline]
2764    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
2765        use crate::fmt::StdFmtWrite;
2766
2767        let precision =
2768            f.precision().map(|p| u8::try_from(p).unwrap_or(u8::MAX));
2769        temporal::DateTimePrinter::new()
2770            .precision(precision)
2771            .print_timestamp_with_offset(
2772                &self.timestamp,
2773                self.offset,
2774                StdFmtWrite(f),
2775            )
2776            .map_err(|_| core::fmt::Error)
2777    }
2778}
2779
2780/// An iterator over periodic timestamps, created by [`Timestamp::series`].
2781///
2782/// It is exhausted when the next value would exceed a [`Span`] or
2783/// [`Timestamp`] value.
2784#[derive(Clone, Debug)]
2785pub struct TimestampSeries {
2786    ts: Timestamp,
2787    duration: Option<SignedDuration>,
2788}
2789
2790impl TimestampSeries {
2791    #[inline]
2792    fn new(ts: Timestamp, period: Span) -> TimestampSeries {
2793        let duration = SignedDuration::try_from(period).ok();
2794        TimestampSeries { ts, duration }
2795    }
2796}
2797
2798impl Iterator for TimestampSeries {
2799    type Item = Timestamp;
2800
2801    #[inline]
2802    fn next(&mut self) -> Option<Timestamp> {
2803        let duration = self.duration?;
2804        let this = self.ts;
2805        self.ts = self.ts.checked_add_duration(duration).ok()?;
2806        Some(this)
2807    }
2808}
2809
2810/// Options for [`Timestamp::checked_add`] and [`Timestamp::checked_sub`].
2811///
2812/// This type provides a way to ergonomically add one of a few different
2813/// duration types to a [`Timestamp`].
2814///
2815/// The main way to construct values of this type is with its `From` trait
2816/// implementations:
2817///
2818/// * `From<Span> for TimestampArithmetic` adds (or subtracts) the given span
2819/// to the receiver timestamp.
2820/// * `From<SignedDuration> for TimestampArithmetic` adds (or subtracts)
2821/// the given signed duration to the receiver timestamp.
2822/// * `From<std::time::Duration> for TimestampArithmetic` adds (or subtracts)
2823/// the given unsigned duration to the receiver timestamp.
2824///
2825/// # Example
2826///
2827/// ```
2828/// use std::time::Duration;
2829///
2830/// use jiff::{SignedDuration, Timestamp, ToSpan};
2831///
2832/// let ts: Timestamp = "2024-02-28T00:00:00Z".parse()?;
2833/// assert_eq!(
2834///     ts.checked_add(48.hours())?,
2835///     "2024-03-01T00:00:00Z".parse()?,
2836/// );
2837/// assert_eq!(
2838///     ts.checked_add(SignedDuration::from_hours(48))?,
2839///     "2024-03-01T00:00:00Z".parse()?,
2840/// );
2841/// assert_eq!(
2842///     ts.checked_add(Duration::from_secs(48 * 60 * 60))?,
2843///     "2024-03-01T00:00:00Z".parse()?,
2844/// );
2845///
2846/// # Ok::<(), Box<dyn std::error::Error>>(())
2847/// ```
2848#[derive(Clone, Copy, Debug)]
2849pub struct TimestampArithmetic {
2850    duration: Duration,
2851}
2852
2853impl TimestampArithmetic {
2854    #[inline]
2855    fn checked_add(self, ts: Timestamp) -> Result<Timestamp, Error> {
2856        match self.duration.to_signed()? {
2857            SDuration::Span(span) => ts.checked_add_span(span),
2858            SDuration::Absolute(sdur) => ts.checked_add_duration(sdur),
2859        }
2860    }
2861
2862    #[inline]
2863    fn saturating_add(self, ts: Timestamp) -> Result<Timestamp, Error> {
2864        let Ok(signed) = self.duration.to_signed() else {
2865            return Ok(Timestamp::MAX);
2866        };
2867        let result = match signed {
2868            SDuration::Span(span) => {
2869                if let Some(err) = span.smallest_non_time_non_zero_unit_error()
2870                {
2871                    return Err(err);
2872                }
2873                ts.checked_add_span(span)
2874            }
2875            SDuration::Absolute(sdur) => ts.checked_add_duration(sdur),
2876        };
2877        Ok(result.unwrap_or_else(|_| {
2878            if self.is_negative() {
2879                Timestamp::MIN
2880            } else {
2881                Timestamp::MAX
2882            }
2883        }))
2884    }
2885
2886    #[inline]
2887    fn checked_neg(self) -> Result<TimestampArithmetic, Error> {
2888        let duration = self.duration.checked_neg()?;
2889        Ok(TimestampArithmetic { duration })
2890    }
2891
2892    #[inline]
2893    fn is_negative(&self) -> bool {
2894        self.duration.is_negative()
2895    }
2896}
2897
2898impl From<Span> for TimestampArithmetic {
2899    fn from(span: Span) -> TimestampArithmetic {
2900        let duration = Duration::from(span);
2901        TimestampArithmetic { duration }
2902    }
2903}
2904
2905impl From<SignedDuration> for TimestampArithmetic {
2906    fn from(sdur: SignedDuration) -> TimestampArithmetic {
2907        let duration = Duration::from(sdur);
2908        TimestampArithmetic { duration }
2909    }
2910}
2911
2912impl From<UnsignedDuration> for TimestampArithmetic {
2913    fn from(udur: UnsignedDuration) -> TimestampArithmetic {
2914        let duration = Duration::from(udur);
2915        TimestampArithmetic { duration }
2916    }
2917}
2918
2919impl<'a> From<&'a Span> for TimestampArithmetic {
2920    fn from(span: &'a Span) -> TimestampArithmetic {
2921        TimestampArithmetic::from(*span)
2922    }
2923}
2924
2925impl<'a> From<&'a SignedDuration> for TimestampArithmetic {
2926    fn from(sdur: &'a SignedDuration) -> TimestampArithmetic {
2927        TimestampArithmetic::from(*sdur)
2928    }
2929}
2930
2931impl<'a> From<&'a UnsignedDuration> for TimestampArithmetic {
2932    fn from(udur: &'a UnsignedDuration) -> TimestampArithmetic {
2933        TimestampArithmetic::from(*udur)
2934    }
2935}
2936
2937/// Options for [`Timestamp::since`] and [`Timestamp::until`].
2938///
2939/// This type provides a way to configure the calculation of
2940/// spans between two [`Timestamp`] values. In particular, both
2941/// `Timestamp::since` and `Timestamp::until` accept anything that implements
2942/// `Into<TimestampDifference>`. There are a few key trait implementations that
2943/// make this convenient:
2944///
2945/// * `From<Timestamp> for TimestampDifference` will construct a
2946/// configuration consisting of just the timestamp. So for example,
2947/// `timestamp1.until(timestamp2)` will return the span from `timestamp1` to
2948/// `timestamp2`.
2949/// * `From<Zoned> for TimestampDifference` will construct a configuration
2950/// consisting of the timestamp from the given zoned datetime. So for example,
2951/// `timestamp.since(zoned)` returns the span from `zoned.to_timestamp()` to
2952/// `timestamp`.
2953/// * `From<(Unit, Timestamp)>` is a convenient way to specify the largest
2954/// units that should be present on the span returned. By default, the largest
2955/// units are seconds. Using this trait implementation is equivalent to
2956/// `TimestampDifference::new(timestamp).largest(unit)`.
2957/// * `From<(Unit, Zoned)>` is like the one above, but with the time from
2958/// the given zoned datetime.
2959///
2960/// One can also provide a `TimestampDifference` value directly. Doing so
2961/// is necessary to use the rounding features of calculating a span. For
2962/// example, setting the smallest unit (defaults to [`Unit::Nanosecond`]), the
2963/// rounding mode (defaults to [`RoundMode::Trunc`]) and the rounding increment
2964/// (defaults to `1`). The defaults are selected such that no rounding occurs.
2965///
2966/// Rounding a span as part of calculating it is provided as a convenience.
2967/// Callers may choose to round the span as a distinct step via
2968/// [`Span::round`].
2969///
2970/// # Example
2971///
2972/// This example shows how to round a span between two timestamps to the
2973/// nearest half-hour, with ties breaking away from zero.
2974///
2975/// ```
2976/// use jiff::{RoundMode, Timestamp, TimestampDifference, ToSpan, Unit};
2977///
2978/// let ts1 = "2024-03-15 08:14:00.123456789Z".parse::<Timestamp>()?;
2979/// let ts2 = "2024-03-22 15:00Z".parse::<Timestamp>()?;
2980/// let span = ts1.until(
2981///     TimestampDifference::new(ts2)
2982///         .smallest(Unit::Minute)
2983///         .largest(Unit::Hour)
2984///         .mode(RoundMode::HalfExpand)
2985///         .increment(30),
2986/// )?;
2987/// assert_eq!(format!("{span:#}"), "175h");
2988///
2989/// // One less minute, and because of the HalfExpand mode, the span would
2990/// // get rounded down.
2991/// let ts2 = "2024-03-22 14:59Z".parse::<Timestamp>()?;
2992/// let span = ts1.until(
2993///     TimestampDifference::new(ts2)
2994///         .smallest(Unit::Minute)
2995///         .largest(Unit::Hour)
2996///         .mode(RoundMode::HalfExpand)
2997///         .increment(30),
2998/// )?;
2999/// assert_eq!(span, 174.hours().minutes(30).fieldwise());
3000///
3001/// # Ok::<(), Box<dyn std::error::Error>>(())
3002/// ```
3003#[derive(Clone, Copy, Debug)]
3004pub struct TimestampDifference {
3005    timestamp: Timestamp,
3006    round: SpanRound<'static>,
3007}
3008
3009impl TimestampDifference {
3010    /// Create a new default configuration for computing the span between
3011    /// the given timestamp and some other time (specified as the receiver in
3012    /// [`Timestamp::since`] or [`Timestamp::until`]).
3013    #[inline]
3014    pub fn new(timestamp: Timestamp) -> TimestampDifference {
3015        // We use truncation rounding by default since it seems that's
3016        // what is generally expected when computing the difference between
3017        // datetimes.
3018        //
3019        // See: https://github.com/tc39/proposal-temporal/issues/1122
3020        let round = SpanRound::new().mode(RoundMode::Trunc);
3021        TimestampDifference { timestamp, round }
3022    }
3023
3024    /// Set the smallest units allowed in the span returned.
3025    ///
3026    /// # Errors
3027    ///
3028    /// The smallest units must be no greater than the largest units. If this
3029    /// is violated, then computing a span with this configuration will result
3030    /// in an error.
3031    ///
3032    /// # Example
3033    ///
3034    /// This shows how to round a span between two timestamps to units no less
3035    /// than seconds.
3036    ///
3037    /// ```
3038    /// use jiff::{RoundMode, Timestamp, TimestampDifference, ToSpan, Unit};
3039    ///
3040    /// let ts1 = "2024-03-15 08:14:02.5001Z".parse::<Timestamp>()?;
3041    /// let ts2 = "2024-03-15T08:16:03.0001Z".parse::<Timestamp>()?;
3042    /// let span = ts1.until(
3043    ///     TimestampDifference::new(ts2)
3044    ///         .smallest(Unit::Second)
3045    ///         .mode(RoundMode::HalfExpand),
3046    /// )?;
3047    /// assert_eq!(span, 121.seconds().fieldwise());
3048    ///
3049    /// // Because of the rounding mode, a small less-than-1-second increase in
3050    /// // the first timestamp can change the result of rounding.
3051    /// let ts1 = "2024-03-15 08:14:02.5002Z".parse::<Timestamp>()?;
3052    /// let span = ts1.until(
3053    ///     TimestampDifference::new(ts2)
3054    ///         .smallest(Unit::Second)
3055    ///         .mode(RoundMode::HalfExpand),
3056    /// )?;
3057    /// assert_eq!(span, 120.seconds().fieldwise());
3058    ///
3059    /// # Ok::<(), Box<dyn std::error::Error>>(())
3060    /// ```
3061    #[inline]
3062    pub fn smallest(self, unit: Unit) -> TimestampDifference {
3063        TimestampDifference { round: self.round.smallest(unit), ..self }
3064    }
3065
3066    /// Set the largest units allowed in the span returned.
3067    ///
3068    /// When a largest unit is not specified, computing a span between
3069    /// timestamps behaves as if it were set to [`Unit::Second`]. Unless
3070    /// [`TimestampDifference::smallest`] is bigger than `Unit::Second`, then
3071    /// the largest unit is set to the smallest unit.
3072    ///
3073    /// # Errors
3074    ///
3075    /// The largest units, when set, must be at least as big as the smallest
3076    /// units (which defaults to [`Unit::Nanosecond`]). If this is violated,
3077    /// then computing a span with this configuration will result in an error.
3078    ///
3079    /// # Example
3080    ///
3081    /// This shows how to round a span between two timestamps to units no
3082    /// bigger than seconds.
3083    ///
3084    /// ```
3085    /// use jiff::{Timestamp, TimestampDifference, ToSpan, Unit};
3086    ///
3087    /// let ts1 = "2024-03-15 08:14Z".parse::<Timestamp>()?;
3088    /// let ts2 = "2030-11-22 08:30Z".parse::<Timestamp>()?;
3089    /// let span = ts1.until(
3090    ///     TimestampDifference::new(ts2).largest(Unit::Second),
3091    /// )?;
3092    /// assert_eq!(format!("{span:#}"), "211076160s");
3093    ///
3094    /// # Ok::<(), Box<dyn std::error::Error>>(())
3095    /// ```
3096    #[inline]
3097    pub fn largest(self, unit: Unit) -> TimestampDifference {
3098        TimestampDifference { round: self.round.largest(unit), ..self }
3099    }
3100
3101    /// Set the rounding mode.
3102    ///
3103    /// This defaults to [`RoundMode::Trunc`] since it's plausible that
3104    /// rounding "up" in the context of computing the span between
3105    /// two timestamps could be surprising in a number of cases. The
3106    /// [`RoundMode::HalfExpand`] mode corresponds to typical rounding you
3107    /// might have learned about in school. But a variety of other rounding
3108    /// modes exist.
3109    ///
3110    /// # Example
3111    ///
3112    /// This shows how to always round "up" towards positive infinity.
3113    ///
3114    /// ```
3115    /// use jiff::{RoundMode, Timestamp, TimestampDifference, ToSpan, Unit};
3116    ///
3117    /// let ts1 = "2024-03-15 08:10Z".parse::<Timestamp>()?;
3118    /// let ts2 = "2024-03-15 08:11Z".parse::<Timestamp>()?;
3119    /// let span = ts1.until(
3120    ///     TimestampDifference::new(ts2)
3121    ///         .smallest(Unit::Hour)
3122    ///         .mode(RoundMode::Ceil),
3123    /// )?;
3124    /// // Only one minute elapsed, but we asked to always round up!
3125    /// assert_eq!(span, 1.hour().fieldwise());
3126    ///
3127    /// // Since `Ceil` always rounds toward positive infinity, the behavior
3128    /// // flips for a negative span.
3129    /// let span = ts1.since(
3130    ///     TimestampDifference::new(ts2)
3131    ///         .smallest(Unit::Hour)
3132    ///         .mode(RoundMode::Ceil),
3133    /// )?;
3134    /// assert_eq!(span, 0.hour().fieldwise());
3135    ///
3136    /// # Ok::<(), Box<dyn std::error::Error>>(())
3137    /// ```
3138    #[inline]
3139    pub fn mode(self, mode: RoundMode) -> TimestampDifference {
3140        TimestampDifference { round: self.round.mode(mode), ..self }
3141    }
3142
3143    /// Set the rounding increment for the smallest unit.
3144    ///
3145    /// The default value is `1`. Other values permit rounding the smallest
3146    /// unit to the nearest integer increment specified. For example, if the
3147    /// smallest unit is set to [`Unit::Minute`], then a rounding increment of
3148    /// `30` would result in rounding in increments of a half hour. That is,
3149    /// the only minute value that could result would be `0` or `30`.
3150    ///
3151    /// # Errors
3152    ///
3153    /// The rounding increment must divide evenly into the next highest unit
3154    /// after the smallest unit configured (and must not be equivalent to it).
3155    /// For example, if the smallest unit is [`Unit::Nanosecond`], then *some*
3156    /// of the valid values for the rounding increment are `1`, `2`, `4`, `5`,
3157    /// `100` and `500`. Namely, any integer that divides evenly into `1,000`
3158    /// nanoseconds since there are `1,000` nanoseconds in the next highest
3159    /// unit (microseconds).
3160    ///
3161    /// The error will occur when computing the span, and not when setting
3162    /// the increment here.
3163    ///
3164    /// # Example
3165    ///
3166    /// This shows how to round the span between two timestamps to the nearest
3167    /// 5 minute increment.
3168    ///
3169    /// ```
3170    /// use jiff::{RoundMode, Timestamp, TimestampDifference, ToSpan, Unit};
3171    ///
3172    /// let ts1 = "2024-03-15 08:19Z".parse::<Timestamp>()?;
3173    /// let ts2 = "2024-03-15 12:52Z".parse::<Timestamp>()?;
3174    /// let span = ts1.until(
3175    ///     TimestampDifference::new(ts2)
3176    ///         .smallest(Unit::Minute)
3177    ///         .increment(5)
3178    ///         .mode(RoundMode::HalfExpand),
3179    /// )?;
3180    /// assert_eq!(span.to_string(), "PT275M");
3181    ///
3182    /// # Ok::<(), Box<dyn std::error::Error>>(())
3183    /// ```
3184    #[inline]
3185    pub fn increment(self, increment: i64) -> TimestampDifference {
3186        TimestampDifference { round: self.round.increment(increment), ..self }
3187    }
3188
3189    /// Returns true if and only if this configuration could change the span
3190    /// via rounding.
3191    #[inline]
3192    fn rounding_may_change_span(&self) -> bool {
3193        self.round.rounding_may_change_span_ignore_largest()
3194    }
3195
3196    /// Returns the span of time from `ts1` to the timestamp in this
3197    /// configuration. The biggest units allowed are determined by the
3198    /// `smallest` and `largest` settings, but defaults to `Unit::Second`.
3199    #[inline]
3200    fn until_with_largest_unit(&self, t1: Timestamp) -> Result<Span, Error> {
3201        let t2 = self.timestamp;
3202        let largest = self
3203            .round
3204            .get_largest()
3205            .unwrap_or_else(|| self.round.get_smallest().max(Unit::Second));
3206        if largest >= Unit::Day {
3207            return Err(err!(
3208                "unit {largest} is not supported when computing the \
3209                 difference between timestamps (must use units smaller \
3210                 than 'day')",
3211                largest = largest.singular(),
3212            ));
3213        }
3214        let nano1 = t1.as_nanosecond_ranged().without_bounds();
3215        let nano2 = t2.as_nanosecond_ranged().without_bounds();
3216        let diff = nano2 - nano1;
3217        // This can fail when `largest` is nanoseconds since not all intervals
3218        // can be represented by a single i64 in units of nanoseconds.
3219        Span::from_invariant_nanoseconds(largest, diff)
3220    }
3221}
3222
3223impl From<Timestamp> for TimestampDifference {
3224    #[inline]
3225    fn from(ts: Timestamp) -> TimestampDifference {
3226        TimestampDifference::new(ts)
3227    }
3228}
3229
3230impl From<Zoned> for TimestampDifference {
3231    #[inline]
3232    fn from(zdt: Zoned) -> TimestampDifference {
3233        TimestampDifference::new(Timestamp::from(zdt))
3234    }
3235}
3236
3237impl<'a> From<&'a Zoned> for TimestampDifference {
3238    #[inline]
3239    fn from(zdt: &'a Zoned) -> TimestampDifference {
3240        TimestampDifference::from(Timestamp::from(zdt))
3241    }
3242}
3243
3244impl From<(Unit, Timestamp)> for TimestampDifference {
3245    #[inline]
3246    fn from((largest, ts): (Unit, Timestamp)) -> TimestampDifference {
3247        TimestampDifference::from(ts).largest(largest)
3248    }
3249}
3250
3251impl From<(Unit, Zoned)> for TimestampDifference {
3252    #[inline]
3253    fn from((largest, zdt): (Unit, Zoned)) -> TimestampDifference {
3254        TimestampDifference::from((largest, Timestamp::from(zdt)))
3255    }
3256}
3257
3258impl<'a> From<(Unit, &'a Zoned)> for TimestampDifference {
3259    #[inline]
3260    fn from((largest, zdt): (Unit, &'a Zoned)) -> TimestampDifference {
3261        TimestampDifference::from((largest, Timestamp::from(zdt)))
3262    }
3263}
3264
3265/// Options for [`Timestamp::round`].
3266///
3267/// This type provides a way to configure the rounding of a timestamp. In
3268/// particular, `Timestamp::round` accepts anything that implements the
3269/// `Into<TimestampRound>` trait. There are some trait implementations that
3270/// therefore make calling `Timestamp::round` in some common cases more
3271/// ergonomic:
3272///
3273/// * `From<Unit> for TimestampRound` will construct a rounding
3274/// configuration that rounds to the unit given. Specifically,
3275/// `TimestampRound::new().smallest(unit)`.
3276/// * `From<(Unit, i64)> for TimestampRound` is like the one above, but also
3277/// specifies the rounding increment for [`TimestampRound::increment`].
3278///
3279/// Note that in the default configuration, no rounding occurs.
3280///
3281/// # Example
3282///
3283/// This example shows how to round a timestamp to the nearest second:
3284///
3285/// ```
3286/// use jiff::{Timestamp, Unit};
3287///
3288/// let ts: Timestamp = "2024-06-20 16:24:59.5Z".parse()?;
3289/// assert_eq!(
3290///     ts.round(Unit::Second)?.to_string(),
3291///     // The second rounds up and causes minutes to increase.
3292///     "2024-06-20T16:25:00Z",
3293/// );
3294///
3295/// # Ok::<(), Box<dyn std::error::Error>>(())
3296/// ```
3297///
3298/// The above makes use of the fact that `Unit` implements
3299/// `Into<TimestampRound>`. If you want to change the rounding mode to, say,
3300/// truncation, then you'll need to construct a `TimestampRound` explicitly
3301/// since there are no convenience `Into` trait implementations for
3302/// [`RoundMode`].
3303///
3304/// ```
3305/// use jiff::{RoundMode, Timestamp, TimestampRound, Unit};
3306///
3307/// let ts: Timestamp = "2024-06-20 16:24:59.5Z".parse()?;
3308/// assert_eq!(
3309///     ts.round(
3310///         TimestampRound::new().smallest(Unit::Second).mode(RoundMode::Trunc),
3311///     )?.to_string(),
3312///     // The second just gets truncated as if it wasn't there.
3313///     "2024-06-20T16:24:59Z",
3314/// );
3315///
3316/// # Ok::<(), Box<dyn std::error::Error>>(())
3317/// ```
3318#[derive(Clone, Copy, Debug)]
3319pub struct TimestampRound {
3320    smallest: Unit,
3321    mode: RoundMode,
3322    increment: i64,
3323}
3324
3325impl TimestampRound {
3326    /// Create a new default configuration for rounding a [`Timestamp`].
3327    #[inline]
3328    pub fn new() -> TimestampRound {
3329        TimestampRound {
3330            smallest: Unit::Nanosecond,
3331            mode: RoundMode::HalfExpand,
3332            increment: 1,
3333        }
3334    }
3335
3336    /// Set the smallest units allowed in the timestamp returned after
3337    /// rounding.
3338    ///
3339    /// Any units below the smallest configured unit will be used, along with
3340    /// the rounding increment and rounding mode, to determine the value of the
3341    /// smallest unit. For example, when rounding `2024-06-20T03:25:30Z` to the
3342    /// nearest minute, the `30` second unit will result in rounding the minute
3343    /// unit of `25` up to `26` and zeroing out everything below minutes.
3344    ///
3345    /// This defaults to [`Unit::Nanosecond`].
3346    ///
3347    /// # Errors
3348    ///
3349    /// The smallest units must be no greater than [`Unit::Hour`].
3350    ///
3351    /// # Example
3352    ///
3353    /// ```
3354    /// use jiff::{Timestamp, TimestampRound, Unit};
3355    ///
3356    /// let ts: Timestamp = "2024-06-20T03:25:30Z".parse()?;
3357    /// assert_eq!(
3358    ///     ts.round(TimestampRound::new().smallest(Unit::Minute))?.to_string(),
3359    ///     "2024-06-20T03:26:00Z",
3360    /// );
3361    /// // Or, utilize the `From<Unit> for TimestampRound` impl:
3362    /// assert_eq!(
3363    ///     ts.round(Unit::Minute)?.to_string(),
3364    ///     "2024-06-20T03:26:00Z",
3365    /// );
3366    ///
3367    /// # Ok::<(), Box<dyn std::error::Error>>(())
3368    /// ```
3369    #[inline]
3370    pub fn smallest(self, unit: Unit) -> TimestampRound {
3371        TimestampRound { smallest: unit, ..self }
3372    }
3373
3374    /// Set the rounding mode.
3375    ///
3376    /// This defaults to [`RoundMode::HalfExpand`], which rounds away from
3377    /// zero. It matches the kind of rounding you might have been taught in
3378    /// school.
3379    ///
3380    /// # Example
3381    ///
3382    /// This shows how to always round timestamps up towards positive infinity.
3383    ///
3384    /// ```
3385    /// use jiff::{RoundMode, Timestamp, TimestampRound, Unit};
3386    ///
3387    /// let ts: Timestamp = "2024-06-20 03:25:01Z".parse()?;
3388    /// assert_eq!(
3389    ///     ts.round(
3390    ///         TimestampRound::new()
3391    ///             .smallest(Unit::Minute)
3392    ///             .mode(RoundMode::Ceil),
3393    ///     )?.to_string(),
3394    ///     "2024-06-20T03:26:00Z",
3395    /// );
3396    ///
3397    /// # Ok::<(), Box<dyn std::error::Error>>(())
3398    /// ```
3399    #[inline]
3400    pub fn mode(self, mode: RoundMode) -> TimestampRound {
3401        TimestampRound { mode, ..self }
3402    }
3403
3404    /// Set the rounding increment for the smallest unit.
3405    ///
3406    /// The default value is `1`. Other values permit rounding the smallest
3407    /// unit to the nearest integer increment specified. For example, if the
3408    /// smallest unit is set to [`Unit::Minute`], then a rounding increment of
3409    /// `30` would result in rounding in increments of a half hour. That is,
3410    /// the only minute value that could result would be `0` or `30`.
3411    ///
3412    /// # Errors
3413    ///
3414    /// The rounding increment, when combined with the smallest unit (which
3415    /// defaults to [`Unit::Nanosecond`]), must divide evenly into `86,400`
3416    /// seconds (one 24-hour civil day). For example, increments of both
3417    /// 45 seconds and 15 minutes are allowed, but 7 seconds and 25 minutes are
3418    /// both not allowed.
3419    ///
3420    /// # Example
3421    ///
3422    /// This example shows how to round a timestamp to the nearest 10 minute
3423    /// increment.
3424    ///
3425    /// ```
3426    /// use jiff::{RoundMode, Timestamp, TimestampRound, Unit};
3427    ///
3428    /// let ts: Timestamp = "2024-06-20 03:24:59Z".parse()?;
3429    /// assert_eq!(
3430    ///     ts.round((Unit::Minute, 10))?.to_string(),
3431    ///     "2024-06-20T03:20:00Z",
3432    /// );
3433    ///
3434    /// # Ok::<(), Box<dyn std::error::Error>>(())
3435    /// ```
3436    #[inline]
3437    pub fn increment(self, increment: i64) -> TimestampRound {
3438        TimestampRound { increment, ..self }
3439    }
3440
3441    /// Does the actual rounding.
3442    pub(crate) fn round(
3443        &self,
3444        timestamp: Timestamp,
3445    ) -> Result<Timestamp, Error> {
3446        let increment =
3447            increment::for_timestamp(self.smallest, self.increment)?;
3448        let nanosecond = timestamp.as_nanosecond_ranged().without_bounds();
3449        let rounded = self.mode.round_by_unit_in_nanoseconds(
3450            nanosecond,
3451            self.smallest,
3452            increment,
3453        );
3454        let nanosecond = UnixNanoseconds::rfrom(rounded);
3455        Ok(Timestamp::from_nanosecond_ranged(nanosecond))
3456    }
3457}
3458
3459impl Default for TimestampRound {
3460    #[inline]
3461    fn default() -> TimestampRound {
3462        TimestampRound::new()
3463    }
3464}
3465
3466impl From<Unit> for TimestampRound {
3467    #[inline]
3468    fn from(unit: Unit) -> TimestampRound {
3469        TimestampRound::default().smallest(unit)
3470    }
3471}
3472
3473impl From<(Unit, i64)> for TimestampRound {
3474    #[inline]
3475    fn from((unit, increment): (Unit, i64)) -> TimestampRound {
3476        TimestampRound::from(unit).increment(increment)
3477    }
3478}
3479
3480#[cfg(test)]
3481mod tests {
3482    use alloc::string::ToString;
3483
3484    use std::io::Cursor;
3485
3486    use crate::{
3487        civil::{self, datetime},
3488        tz::Offset,
3489        ToSpan,
3490    };
3491
3492    use super::*;
3493
3494    fn mktime(seconds: i64, nanos: i32) -> Timestamp {
3495        Timestamp::new(seconds, nanos).unwrap()
3496    }
3497
3498    fn mkdt(
3499        year: i16,
3500        month: i8,
3501        day: i8,
3502        hour: i8,
3503        minute: i8,
3504        second: i8,
3505        nano: i32,
3506    ) -> civil::DateTime {
3507        let date = civil::Date::new(year, month, day).unwrap();
3508        let time = civil::Time::new(hour, minute, second, nano).unwrap();
3509        civil::DateTime::from_parts(date, time)
3510    }
3511
3512    #[test]
3513    fn to_datetime_specific_examples() {
3514        let tests = [
3515            ((UnixSeconds::MIN_REPR, 0), (-9999, 1, 2, 1, 59, 59, 0)),
3516            (
3517                (UnixSeconds::MIN_REPR + 1, -999_999_999),
3518                (-9999, 1, 2, 1, 59, 59, 1),
3519            ),
3520            ((-1, 1), (1969, 12, 31, 23, 59, 59, 1)),
3521            ((UnixSeconds::MAX_REPR, 0), (9999, 12, 30, 22, 0, 0, 0)),
3522            ((UnixSeconds::MAX_REPR - 1, 0), (9999, 12, 30, 21, 59, 59, 0)),
3523            (
3524                (UnixSeconds::MAX_REPR - 1, 999_999_999),
3525                (9999, 12, 30, 21, 59, 59, 999_999_999),
3526            ),
3527            (
3528                (UnixSeconds::MAX_REPR, 999_999_999),
3529                (9999, 12, 30, 22, 0, 0, 999_999_999),
3530            ),
3531            ((-2, -1), (1969, 12, 31, 23, 59, 57, 999_999_999)),
3532            ((-86398, -1), (1969, 12, 31, 0, 0, 1, 999_999_999)),
3533            ((-86399, -1), (1969, 12, 31, 0, 0, 0, 999_999_999)),
3534            ((-86400, -1), (1969, 12, 30, 23, 59, 59, 999_999_999)),
3535        ];
3536        for (t, dt) in tests {
3537            let timestamp = mktime(t.0, t.1);
3538            let datetime = mkdt(dt.0, dt.1, dt.2, dt.3, dt.4, dt.5, dt.6);
3539            assert_eq!(
3540                Offset::UTC.to_datetime(timestamp),
3541                datetime,
3542                "timestamp: {t:?}"
3543            );
3544            assert_eq!(
3545                timestamp,
3546                datetime.to_zoned(TimeZone::UTC).unwrap().timestamp(),
3547                "datetime: {datetime:?}"
3548            );
3549        }
3550    }
3551
3552    #[test]
3553    fn to_datetime_many_seconds_in_some_days() {
3554        let days = [
3555            i64::from(t::UnixEpochDay::MIN_REPR),
3556            -1000,
3557            -5,
3558            23,
3559            2000,
3560            i64::from(t::UnixEpochDay::MAX_REPR),
3561        ];
3562        let seconds = [
3563            -86_400, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4,
3564            5, 6, 7, 8, 9, 10, 86_400,
3565        ];
3566        let nanos = [0, 1, 5, 999_999_999];
3567        for day in days {
3568            let midpoint = day * 86_400;
3569            for second in seconds {
3570                let second = midpoint + second;
3571                if !UnixSeconds::contains(second) {
3572                    continue;
3573                }
3574                for nano in nanos {
3575                    if second == UnixSeconds::MIN_REPR && nano != 0 {
3576                        continue;
3577                    }
3578                    let t = Timestamp::new(second, nano).unwrap();
3579                    let Ok(got) =
3580                        Offset::UTC.to_datetime(t).to_zoned(TimeZone::UTC)
3581                    else {
3582                        continue;
3583                    };
3584                    assert_eq!(t, got.timestamp());
3585                }
3586            }
3587        }
3588    }
3589
3590    #[test]
3591    fn invalid_time() {
3592        assert!(Timestamp::new(UnixSeconds::MIN_REPR, -1).is_err());
3593        assert!(Timestamp::new(UnixSeconds::MIN_REPR, -999_999_999).is_err());
3594        // These are greater than the minimum and thus okay!
3595        assert!(Timestamp::new(UnixSeconds::MIN_REPR, 1).is_ok());
3596        assert!(Timestamp::new(UnixSeconds::MIN_REPR, 999_999_999).is_ok());
3597    }
3598
3599    #[cfg(target_pointer_width = "64")]
3600    #[test]
3601    fn timestamp_size() {
3602        #[cfg(debug_assertions)]
3603        {
3604            assert_eq!(40, core::mem::size_of::<Timestamp>());
3605        }
3606        #[cfg(not(debug_assertions))]
3607        {
3608            assert_eq!(16, core::mem::size_of::<Timestamp>());
3609        }
3610    }
3611
3612    #[test]
3613    fn nanosecond_roundtrip_boundaries() {
3614        let inst = Timestamp::MIN;
3615        let nanos = inst.as_nanosecond_ranged();
3616        assert_eq!(0, nanos % t::NANOS_PER_SECOND);
3617        let got = Timestamp::from_nanosecond_ranged(nanos);
3618        assert_eq!(inst, got);
3619
3620        let inst = Timestamp::MAX;
3621        let nanos = inst.as_nanosecond_ranged();
3622        assert_eq!(
3623            FractionalNanosecond::MAX_SELF,
3624            nanos % t::NANOS_PER_SECOND
3625        );
3626        let got = Timestamp::from_nanosecond_ranged(nanos);
3627        assert_eq!(inst, got);
3628    }
3629
3630    #[test]
3631    fn timestamp_saturating_add() {
3632        insta::assert_snapshot!(
3633            Timestamp::MIN.saturating_add(Span::new().days(1)).unwrap_err(),
3634            @"saturating `Timestamp` arithmetic requires only time units: operation can only be performed with units of hours or smaller, but found non-zero day units (operations on `Timestamp`, `tz::Offset` and `civil::Time` don't support calendar units in a `Span`)",
3635        )
3636    }
3637
3638    #[test]
3639    fn timestamp_saturating_sub() {
3640        insta::assert_snapshot!(
3641            Timestamp::MAX.saturating_sub(Span::new().days(1)).unwrap_err(),
3642            @"saturating `Timestamp` arithmetic requires only time units: operation can only be performed with units of hours or smaller, but found non-zero day units (operations on `Timestamp`, `tz::Offset` and `civil::Time` don't support calendar units in a `Span`)",
3643        )
3644    }
3645
3646    quickcheck::quickcheck! {
3647        fn prop_unix_seconds_roundtrip(t: Timestamp) -> quickcheck::TestResult {
3648            let dt = t.to_zoned(TimeZone::UTC).datetime();
3649            let Ok(got) = dt.to_zoned(TimeZone::UTC) else {
3650                return quickcheck::TestResult::discard();
3651            };
3652            quickcheck::TestResult::from_bool(t == got.timestamp())
3653        }
3654
3655        fn prop_nanos_roundtrip_unix_ranged(t: Timestamp) -> bool {
3656            let nanos = t.as_nanosecond_ranged();
3657            let got = Timestamp::from_nanosecond_ranged(nanos);
3658            t == got
3659        }
3660
3661        fn prop_nanos_roundtrip_unix(t: Timestamp) -> bool {
3662            let nanos = t.as_nanosecond();
3663            let got = Timestamp::from_nanosecond(nanos).unwrap();
3664            t == got
3665        }
3666
3667        fn timestamp_constant_and_new_are_same1(t: Timestamp) -> bool {
3668            let got = Timestamp::constant(t.as_second(), t.subsec_nanosecond());
3669            t == got
3670        }
3671
3672        fn timestamp_constant_and_new_are_same2(
3673            secs: i64,
3674            nanos: i32
3675        ) -> quickcheck::TestResult {
3676            let Ok(ts) = Timestamp::new(secs, nanos) else {
3677                return quickcheck::TestResult::discard();
3678            };
3679            let got = Timestamp::constant(secs, nanos);
3680            quickcheck::TestResult::from_bool(ts == got)
3681        }
3682    }
3683
3684    /// A `serde` deserializer compatibility test.
3685    ///
3686    /// Serde YAML used to be unable to deserialize `jiff` types,
3687    /// as deserializing from bytes is not supported by the deserializer.
3688    ///
3689    /// - <https://github.com/BurntSushi/jiff/issues/138>
3690    /// - <https://github.com/BurntSushi/jiff/discussions/148>
3691    #[test]
3692    fn timestamp_deserialize_yaml() {
3693        let expected = datetime(2024, 10, 31, 16, 33, 53, 123456789)
3694            .to_zoned(TimeZone::UTC)
3695            .unwrap()
3696            .timestamp();
3697
3698        let deserialized: Timestamp =
3699            serde_yaml::from_str("2024-10-31T16:33:53.123456789+00:00")
3700                .unwrap();
3701
3702        assert_eq!(deserialized, expected);
3703
3704        let deserialized: Timestamp = serde_yaml::from_slice(
3705            "2024-10-31T16:33:53.123456789+00:00".as_bytes(),
3706        )
3707        .unwrap();
3708
3709        assert_eq!(deserialized, expected);
3710
3711        let cursor = Cursor::new(b"2024-10-31T16:33:53.123456789+00:00");
3712        let deserialized: Timestamp = serde_yaml::from_reader(cursor).unwrap();
3713
3714        assert_eq!(deserialized, expected);
3715    }
3716
3717    #[test]
3718    fn timestamp_precision_loss() {
3719        let ts1: Timestamp =
3720            "2025-01-25T19:32:21.783444592+01:00".parse().unwrap();
3721        let span = 1.second();
3722        let ts2 = ts1 + span;
3723        assert_eq!(ts2.to_string(), "2025-01-25T18:32:22.783444592Z");
3724        assert_eq!(ts1, ts2 - span, "should be reversible");
3725    }
3726}