t2t_core/timestamp.rs
1//! Points on the wall clock and on International Atomic Time.
2
3use derive_more::Debug;
4#[cfg(feature = "zerocopy-08")]
5use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
6
7/// A point on the wall clock, in nanoseconds since the Unix epoch.
8///
9/// It names a moment another machine can name too, which is what makes it the stamp to capture
10/// with; the clock behind it may step, so a span that must never run backwards is measured on an
11/// [`Uptime`](crate::Uptime) instead. It holds 1677-09-21 to 2262-04-11, and its operators saturate
12/// at either end. It is written as RFC 3339 in UTC, and parses back from it.
13///
14/// # Examples
15/// ```
16/// use t2t_core::{Timedelta, Timestamp};
17///
18/// let published = Timestamp::from_secs(1_700_000_000);
19/// let captured = published + Timedelta::from_micros(850);
20///
21/// assert_eq!(captured - published, Timedelta::from_micros(850), "a point minus a point");
22/// assert_eq!(format!("{captured:.6}"), "2023-11-14T22:13:20.000850Z", "to the microsecond");
23/// let minute = captured.floor(Timedelta::MINUTE);
24/// assert_eq!(format!("{minute:.0}"), "2023-11-14T22:13:00Z", "the minute it falls in");
25/// ```
26#[repr(transparent)]
27#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
28#[debug("{self}")]
29#[cfg_attr(feature = "zerocopy-08", derive(FromBytes, IntoBytes, Immutable, KnownLayout))]
30pub struct Timestamp(pub(crate) i64);
31
32impl Timestamp {
33 /// `1970-01-01T00:00:00Z`, the point the count runs from.
34 pub const UNIX_EPOCH: Self = Self(0);
35}
36
37/// A point on International Atomic Time, in nanoseconds since 1970-01-01T00:00:00 TAI.
38///
39/// TAI never steps for a leap second, which is why PTP keeps it, and runs ahead of UTC by every
40/// leap second since 1972. The offset between them is the one the kernel holds, which a time
41/// service sets, so a `TaiTimestamp` never mixes with a [`Timestamp`]. It is written as RFC 3339's
42/// date and time in the zone `TAI`, and parses back from it.
43///
44/// # Examples
45/// ```
46/// use t2t_core::{TaiTimestamp, Timedelta};
47///
48/// let synced: TaiTimestamp = "2026-09-16T07:46:12.5 TAI".parse()?;
49/// let next = synced + Timedelta::from_millis(500);
50///
51/// assert_eq!(next.to_string(), "2026-09-16T07:46:13.000000000 TAI", "half a second on");
52/// # Ok::<(), t2t_core::ParseTaiTimestampError>(())
53/// ```
54#[repr(transparent)]
55#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
56#[debug("{self}")]
57#[cfg_attr(feature = "zerocopy-08", derive(FromBytes, IntoBytes, Immutable, KnownLayout))]
58pub struct TaiTimestamp(pub(crate) i64);