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