templar_primitives/
time.rs

1use crate::strnum::SU64;
2use alloc::fmt;
3#[cfg(feature = "schemars")]
4use alloc::string::String;
5#[cfg(any(feature = "borsh", feature = "schemars"))]
6use alloc::string::ToString;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10#[cfg_attr(feature = "serde", serde(transparent))]
11#[cfg_attr(
12    feature = "borsh",
13    derive(borsh::BorshSerialize, borsh::BorshDeserialize, borsh::BorshSchema)
14)]
15pub struct Nanoseconds(SU64);
16
17#[cfg(feature = "schemars")]
18impl schemars::JsonSchema for Nanoseconds {
19    fn schema_name() -> String {
20        "Nanoseconds".to_string()
21    }
22
23    fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
24        let mut schema = schemars::schema::SchemaObject::default();
25        schema.instance_type = Some(schemars::schema::InstanceType::String.into());
26        schema.metadata().description =
27            Some("nanoseconds represented as a decimal string".to_string());
28        schema.into()
29    }
30}
31
32impl Nanoseconds {
33    pub const fn zero() -> Self {
34        Self(SU64::new(0))
35    }
36
37    /// Creates a `Nanoseconds` value from nanoseconds.
38    pub const fn from_ns(value: u64) -> Self {
39        Self(SU64::new(value))
40    }
41
42    /// Creates a `Nanoseconds` value from microseconds.
43    pub const fn from_micros(value: u64) -> Self {
44        Self(SU64::new(value.saturating_mul(1_000)))
45    }
46
47    /// Creates a `Nanoseconds` value from milliseconds.
48    pub const fn from_ms(value: u64) -> Self {
49        Self(SU64::new(value.saturating_mul(1_000_000)))
50    }
51
52    /// Creates a `Nanoseconds` value from seconds.
53    pub const fn from_secs(value: u64) -> Self {
54        Self(SU64::new(value.saturating_mul(1_000_000_000)))
55    }
56
57    /// Creates a `Nanoseconds` value from seconds when the conversion fits.
58    pub const fn checked_from_secs(value: u64) -> Option<Self> {
59        match value.checked_mul(1_000_000_000) {
60            Some(value) => Some(Self(SU64::new(value))),
61            None => None,
62        }
63    }
64
65    /// Returns the value as seconds, truncated.
66    pub const fn as_secs(&self) -> u64 {
67        self.0 .0 / 1_000_000_000
68    }
69
70    /// Returns the value as milliseconds, truncated.
71    pub const fn as_ms(&self) -> u64 {
72        self.0 .0 / 1_000_000
73    }
74
75    /// Returns the value as microseconds, truncated.
76    pub const fn as_micros(&self) -> u64 {
77        self.0 .0 / 1_000
78    }
79
80    /// Returns the value as nanoseconds.
81    pub const fn as_ns(&self) -> u64 {
82        self.0 .0
83    }
84
85    #[must_use]
86    pub const fn saturating_add(self, rhs: Self) -> Self {
87        Self(SU64::new(self.0 .0.saturating_add(rhs.0 .0)))
88    }
89
90    #[must_use]
91    pub const fn saturating_sub(self, rhs: Self) -> Self {
92        Self(SU64::new(self.0 .0.saturating_sub(rhs.0 .0)))
93    }
94}
95
96impl fmt::Display for Nanoseconds {
97    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98        write!(f, "{}ns", self.as_ns())
99    }
100}
101
102#[cfg(feature = "near")]
103mod near {
104    impl super::Nanoseconds {
105        pub fn near_timestamp() -> Self {
106            Self::from_ns(near_sdk::env::block_timestamp())
107        }
108    }
109}
110
111#[cfg(feature = "redstone")]
112mod redstone {
113    impl From<redstone::TimestampMillis> for super::Nanoseconds {
114        fn from(value: redstone::TimestampMillis) -> Self {
115            Self::from_ms(value.as_millis())
116        }
117    }
118
119    impl From<super::Nanoseconds> for redstone::TimestampMillis {
120        fn from(value: super::Nanoseconds) -> Self {
121            Self::from_millis(value.as_ms())
122        }
123    }
124}
125#[cfg(test)]
126mod tests {
127    use super::Nanoseconds;
128
129    #[test]
130    fn checked_seconds_reject_nanosecond_overflow() {
131        assert_eq!(
132            Nanoseconds::checked_from_secs(u64::MAX / 1_000_000_000),
133            Some(Nanoseconds::from_ns(
134                u64::MAX / 1_000_000_000 * 1_000_000_000
135            ))
136        );
137        assert_eq!(
138            Nanoseconds::checked_from_secs(u64::MAX / 1_000_000_000 + 1),
139            None
140        );
141    }
142}