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