levana_perpswap_cosmos/
time.rs1use crate::error::{ErrorDomain, ErrorId, PerpError};
3use crate::prelude::*;
4use anyhow::Result;
5#[cfg(feature = "chrono")]
6use chrono::{DateTime, TimeZone, Utc};
7use cosmwasm_std::{Decimal256, Timestamp as CWTimestamp};
8use cw_storage_plus::{KeyDeserialize, Prefixer, PrimaryKey};
9use schemars::JsonSchema;
10use serde::de::Visitor;
11use serde::{Deserialize, Serialize};
12use std::fmt::{Display, Formatter};
13use std::ops::{Add, Div, Mul, Sub};
14
15#[derive(Debug, Clone, Default, Copy, Eq, PartialEq, Ord, PartialOrd, JsonSchema, Hash)]
22#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
23pub struct Timestamp(#[schemars(with = "String")] u64);
24
25impl Display for Timestamp {
26 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
27 let whole = self.0 / 1_000_000_000;
28 let fractional = self.0 % 1_000_000_000;
29 write!(f, "{}.{:09}", whole, fractional)
30 }
31}
32
33impl Serialize for Timestamp {
34 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
35 where
36 S: serde::Serializer,
37 {
38 serializer.serialize_str(&self.0.to_string())
39 }
40}
41
42impl<'de> Deserialize<'de> for Timestamp {
43 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
44 where
45 D: serde::Deserializer<'de>,
46 {
47 deserializer.deserialize_str(NanoVisitor)
48 }
49}
50
51struct NanoVisitor;
52
53impl<'de> Visitor<'de> for NanoVisitor {
54 type Value = Timestamp;
55
56 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
57 formatter.write_str("nanoseconds since epoch, string-encoded")
58 }
59
60 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
61 where
62 E: serde::de::Error,
63 {
64 match v.parse::<u64>() {
65 Ok(v) => Ok(Timestamp(v)),
66 Err(e) => Err(E::custom(format!("invalid Nano '{v}' - {e}"))),
67 }
68 }
69}
70
71impl Timestamp {
72 pub fn from_nanos(nanos: u64) -> Self {
75 Timestamp(nanos)
76 }
77
78 pub fn from_seconds(seconds: u64) -> Self {
81 Timestamp(seconds * 1_000_000_000)
82 }
83
84 pub fn from_millis(millis: u64) -> Self {
86 Timestamp(millis * 1_000_000)
87 }
88
89 pub fn plus_seconds(self, secs: u64) -> Self {
91 self + Duration::from_seconds(secs)
92 }
93
94 pub fn checked_sub(self, rhs: Self, desc: &str) -> Result<Duration> {
98 #[derive(serde::Serialize)]
99 struct Data {
100 lhs: Timestamp,
101 rhs: Timestamp,
102 desc: String,
103 }
104 let data = Data {
105 lhs: self,
106 rhs,
107 desc: desc.to_owned(),
108 };
109 match self.0.checked_sub(rhs.0) {
110 Some(x) => Ok(Duration(x)),
111 None => Err(anyhow!(PerpError {
112 id: ErrorId::TimestampSubtractUnderflow,
113 domain: ErrorDomain::Default,
114 description: format!(
115 "Invalid timestamp subtraction during. Action: {desc}. Values: {} - {}",
116 data.lhs, data.rhs
117 ),
118 data: Some(data),
119 })),
120 }
121 }
122
123 #[cfg(feature = "chrono")]
124 pub fn try_into_chrono_datetime(self) -> Result<DateTime<Utc>> {
126 let secs = self.0 / 1_000_000_000;
127 let nanos = self.0 % 1_000_000_000;
128
129 Utc.timestamp_opt(secs.try_into()?, nanos.try_into()?)
130 .single()
131 .with_context(|| format!("Could not convert {self} into DateTime<Utc>"))
132 }
133}
134
135impl From<Timestamp> for CWTimestamp {
140 fn from(Timestamp(nanos): Timestamp) -> Self {
141 CWTimestamp::from_nanos(nanos)
142 }
143}
144
145impl From<CWTimestamp> for Timestamp {
146 fn from(timestamp: CWTimestamp) -> Self {
147 Timestamp(timestamp.nanos())
148 }
149}
150
151impl<'a> PrimaryKey<'a> for Timestamp {
152 type Prefix = ();
153 type SubPrefix = ();
154 type Suffix = Timestamp;
155 type SuperSuffix = Timestamp;
156
157 fn key(&self) -> Vec<cw_storage_plus::Key> {
158 self.0.key()
159 }
160}
161
162impl KeyDeserialize for Timestamp {
163 type Output = Timestamp;
164
165 const KEY_ELEMS: u16 = 1;
166
167 fn from_vec(value: Vec<u8>) -> cosmwasm_std::StdResult<Self::Output> {
168 u64::from_vec(value).map(Timestamp)
169 }
170}
171
172impl<'a> Prefixer<'a> for Timestamp {
173 fn prefix(&self) -> Vec<cw_storage_plus::Key> {
174 self.0.prefix()
175 }
176}
177
178#[derive(
180 Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, JsonSchema,
181)]
182pub struct Duration(u64);
183
184impl Duration {
185 pub(crate) fn from_nanos(nanos: u64) -> Self {
187 Duration(nanos)
188 }
189
190 pub fn as_nanos(&self) -> u64 {
192 self.0
193 }
194
195 pub fn as_ms_number_lossy(&self) -> Number {
199 Number::from(self.0 / 1_000_000)
200 }
201
202 pub fn as_ms_decimal_lossy(&self) -> Decimal256 {
206 Decimal256::from_atomics(self.0, 6)
207 .expect("as_ms_decimal_lossy failed, but range won't allow that to happen")
208 }
209
210 pub const fn from_seconds(seconds: u64) -> Self {
212 Duration(seconds * 1_000_000_000)
213 }
214}
215
216impl Add<Duration> for Timestamp {
221 type Output = Timestamp;
222
223 fn add(self, rhs: Duration) -> Self::Output {
224 Timestamp(self.0 + rhs.0)
225 }
226}
227
228impl Sub<Duration> for Duration {
229 type Output = Duration;
230
231 fn sub(self, rhs: Duration) -> Self::Output {
232 Duration(self.0 - rhs.0)
233 }
234}
235
236impl Add<Duration> for Duration {
237 type Output = Duration;
238
239 fn add(self, rhs: Duration) -> Self::Output {
240 Duration(self.0 + rhs.0)
241 }
242}
243
244impl Mul<u64> for Duration {
245 type Output = Duration;
246
247 fn mul(self, rhs: u64) -> Self::Output {
248 Duration(self.0 * rhs)
249 }
250}
251
252impl Div<u64> for Duration {
253 type Output = Duration;
254
255 fn div(self, rhs: u64) -> Self::Output {
256 Duration(self.0 / rhs)
257 }
258}
259
260impl Div<Duration> for Duration {
262 type Output = u64;
263
264 fn div(self, rhs: Self) -> Self::Output {
265 self.0 / rhs.0
266 }
267}
268
269impl FromStr for Timestamp {
270 type Err = PerpError;
271
272 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
273 let err = |msg: &str| -> PerpError {
274 PerpError::new(
275 ErrorId::Conversion,
276 ErrorDomain::Default,
277 format!("error converting {} to Timestamp, {}", s, msg),
278 )
279 };
280
281 let (seconds, nanos) = s
282 .split_once('.')
283 .ok_or_else(|| err("missing decimal point"))?;
284 let seconds = seconds.parse().map_err(|_| err("unable to parse second"))?;
285 let nanos = nanos.parse().map_err(|_| err("unable to parse nanos"))?;
286
287 let timestamp = Timestamp::from_seconds(seconds) + Duration::from_nanos(nanos);
288
289 Ok(timestamp)
290 }
291}