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