andromeda_std/common/
milliseconds.rs

1use cosmwasm_schema::cw_serde;
2use cosmwasm_std::{BlockInfo, Timestamp};
3use cw20::Expiration;
4
5#[cw_serde]
6#[derive(Default, Eq, PartialOrd, Copy)]
7/// Represents time in milliseconds.
8pub struct Milliseconds(pub u64);
9pub type MillisecondsDuration = Milliseconds;
10pub type MillisecondsExpiration = Milliseconds;
11
12impl Milliseconds {
13    pub fn is_expired(&self, block: &BlockInfo) -> bool {
14        let time = block.time.seconds() * 1000;
15        self.0 <= time
16    }
17
18    pub fn is_in_past(&self, block: &BlockInfo) -> bool {
19        let time = block.time.seconds() * 1000;
20        self.0 < time
21    }
22
23    #[inline]
24    pub fn zero() -> Milliseconds {
25        Milliseconds(0)
26    }
27
28    #[inline]
29    pub fn is_zero(&self) -> bool {
30        self.0 == 0
31    }
32
33    #[inline]
34    pub fn from_seconds(seconds: u64) -> Milliseconds {
35        if seconds > u64::MAX / 1000 {
36            panic!("Overflow: Cannot convert seconds to milliseconds")
37        }
38
39        Milliseconds(seconds * 1000)
40    }
41
42    #[inline]
43    pub fn from_nanos(nanos: u64) -> Milliseconds {
44        Milliseconds(nanos / 1000000)
45    }
46
47    #[inline]
48    pub fn milliseconds(&self) -> u64 {
49        self.0
50    }
51
52    #[inline]
53    pub fn seconds(&self) -> u64 {
54        self.0 / 1000
55    }
56
57    #[inline]
58    pub fn nanos(&self) -> u64 {
59        if self.0 > u64::MAX / 1000000 {
60            panic!("Overflow: Cannot convert milliseconds time to nanoseconds")
61        }
62        self.0 * 1000000
63    }
64
65    pub fn add_milliseconds(&mut self, milliseconds: Milliseconds) {
66        self.0 += milliseconds.0;
67    }
68
69    pub fn subtract_milliseconds(&mut self, milliseconds: Milliseconds) {
70        self.0 -= milliseconds.0;
71    }
72
73    pub fn plus_milliseconds(self, milliseconds: Milliseconds) -> Milliseconds {
74        Milliseconds(self.0 + milliseconds.0)
75    }
76
77    pub fn minus_milliseconds(self, milliseconds: Milliseconds) -> Milliseconds {
78        Milliseconds(self.0 - milliseconds.0)
79    }
80
81    pub fn add_seconds(&mut self, seconds: u64) {
82        self.0 += seconds * 1000;
83    }
84
85    pub fn subtract_seconds(&mut self, seconds: u64) {
86        if seconds > self.0 / 1000 {
87            panic!("Overflow: Cannot subtract seconds from milliseconds")
88        }
89
90        self.0 -= seconds * 1000;
91    }
92
93    pub fn plus_seconds(self, seconds: u64) -> Milliseconds {
94        Milliseconds(self.0 + seconds * 1000)
95    }
96
97    pub fn minus_seconds(self, seconds: u64) -> Milliseconds {
98        Milliseconds(self.0 - seconds * 1000)
99    }
100}
101
102impl From<Milliseconds> for String {
103    fn from(time: Milliseconds) -> String {
104        time.0.to_string()
105    }
106}
107
108impl From<Milliseconds> for Timestamp {
109    fn from(time: Milliseconds) -> Timestamp {
110        Timestamp::from_nanos(time.nanos())
111    }
112}
113
114impl From<Milliseconds> for Expiration {
115    fn from(time: Milliseconds) -> Expiration {
116        Expiration::AtTime(time.into())
117    }
118}
119
120impl std::fmt::Display for Milliseconds {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        write!(f, "{}", self.0)
123    }
124}
125
126#[cfg(test)]
127mod test {
128    use cosmwasm_std::testing::mock_env;
129
130    use super::*;
131
132    struct IsExpiredTestCase {
133        name: &'static str,
134        input: u64,
135        curr_time: u64,
136        is_expired: bool,
137    }
138
139    #[test]
140    fn test_is_expired() {
141        let test_cases: Vec<IsExpiredTestCase> = vec![
142            IsExpiredTestCase {
143                name: "valid expiration time (expired)",
144                input: 0,
145                curr_time: 1,
146                is_expired: true,
147            },
148            IsExpiredTestCase {
149                name: "valid expiration time (not expired)",
150                input: 1,
151                curr_time: 0,
152                is_expired: false,
153            },
154            IsExpiredTestCase {
155                name: "same time (expired)",
156                input: 0,
157                curr_time: 0,
158                is_expired: true,
159            },
160        ];
161
162        for test in test_cases {
163            let input = Milliseconds(test.input);
164            let curr_time = Milliseconds(test.curr_time);
165            let mut env = mock_env();
166            env.block.time = curr_time.into();
167
168            let output = input.is_expired(&env.block);
169
170            assert_eq!(test.is_expired, output, "Test failed: {}", test.name)
171        }
172    }
173}