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