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