templar_primitives/
time.rs1use 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 pub const fn from_ns(value: u64) -> Self {
39 Self(SU64::new(value))
40 }
41
42 pub const fn from_micros(value: u64) -> Self {
44 Self(SU64::new(value.saturating_mul(1_000)))
45 }
46
47 pub const fn from_ms(value: u64) -> Self {
49 Self(SU64::new(value.saturating_mul(1_000_000)))
50 }
51
52 pub const fn from_secs(value: u64) -> Self {
54 Self(SU64::new(value.saturating_mul(1_000_000_000)))
55 }
56
57 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 pub const fn as_secs(&self) -> u64 {
67 self.0 .0 / 1_000_000_000
68 }
69
70 pub const fn as_ms(&self) -> u64 {
72 self.0 .0 / 1_000_000
73 }
74
75 pub const fn as_micros(&self) -> u64 {
77 self.0 .0 / 1_000
78 }
79
80 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}