Skip to main content

r402_core/wire/
timestamp.rs

1//! Stringified Unix timestamp.
2
3use std::fmt::{self, Display, Formatter};
4use std::ops::Add;
5use std::time::SystemTime;
6
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8
9/// A Unix timestamp in seconds since the epoch (1970-01-01T00:00:00Z).
10///
11/// The x402 wire format stringifies integer timestamps to guarantee
12/// cross-language precision (JavaScript's `Number` type cannot precisely
13/// represent all `u64` values). [`UnixTimestamp`] serializes as a JSON
14/// string and deserializes from either a JSON string or number.
15///
16/// # Examples
17///
18/// ```
19/// use r402_core::wire::UnixTimestamp;
20///
21/// let ts = UnixTimestamp::from_secs(1_700_000_000);
22/// assert_eq!(ts.as_secs(), 1_700_000_000);
23/// assert_eq!((ts + 3600).as_secs(), 1_700_003_600);
24/// assert_eq!(serde_json::to_string(&ts).unwrap(), r#""1700000000""#);
25/// ```
26#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)]
27pub struct UnixTimestamp(u64);
28
29impl UnixTimestamp {
30    /// Constructs a timestamp from raw Unix seconds.
31    #[must_use]
32    pub const fn from_secs(secs: u64) -> Self {
33        Self(secs)
34    }
35
36    /// Returns the current system clock as a [`UnixTimestamp`].
37    ///
38    /// Falls back to the epoch if the clock is before 1970.
39    #[must_use]
40    pub fn now() -> Self {
41        let secs = SystemTime::now()
42            .duration_since(SystemTime::UNIX_EPOCH)
43            .map(|d| d.as_secs())
44            .unwrap_or_default();
45        Self(secs)
46    }
47
48    /// Returns the timestamp as raw Unix seconds.
49    #[must_use]
50    pub const fn as_secs(self) -> u64 {
51        self.0
52    }
53}
54
55impl Display for UnixTimestamp {
56    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
57        Display::fmt(&self.0, f)
58    }
59}
60
61impl Add<u64> for UnixTimestamp {
62    type Output = Self;
63    fn add(self, rhs: u64) -> Self::Output {
64        Self(self.0.saturating_add(rhs))
65    }
66}
67
68impl Serialize for UnixTimestamp {
69    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
70        serializer.collect_str(&self.0)
71    }
72}
73
74impl<'de> Deserialize<'de> for UnixTimestamp {
75    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
76        struct Visitor;
77
78        impl serde::de::Visitor<'_> for Visitor {
79            type Value = UnixTimestamp;
80
81            fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result {
82                f.write_str("a non-negative integer or its string representation")
83            }
84
85            fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<Self::Value, E> {
86                Ok(UnixTimestamp(v))
87            }
88
89            fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
90                v.parse::<u64>()
91                    .map(UnixTimestamp)
92                    .map_err(|_| E::custom("expected non-negative integer"))
93            }
94        }
95
96        deserializer.deserialize_any(Visitor)
97    }
98}