prov_cosmwasm_std/
timestamp.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use std::fmt;
4
5use crate::math::Uint64;
6
7/// A point in time in nanosecond precision.
8///
9/// This type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.
10///
11/// ## Examples
12///
13/// ```
14/// # use prov_cosmwasm_std::Timestamp;
15/// let ts = Timestamp::from_nanos(1_000_000_202);
16/// assert_eq!(ts.nanos(), 1_000_000_202);
17/// assert_eq!(ts.seconds(), 1);
18/// assert_eq!(ts.subsec_nanos(), 202);
19///
20/// let ts = ts.plus_seconds(2);
21/// assert_eq!(ts.nanos(), 3_000_000_202);
22/// assert_eq!(ts.seconds(), 3);
23/// assert_eq!(ts.subsec_nanos(), 202);
24/// ```
25#[derive(
26    Serialize, Deserialize, Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema,
27)]
28pub struct Timestamp(Uint64);
29
30impl Timestamp {
31    /// Creates a timestamp from nanoseconds since epoch
32    pub const fn from_nanos(nanos_since_epoch: u64) -> Self {
33        Timestamp(Uint64::new(nanos_since_epoch))
34    }
35
36    /// Creates a timestamp from seconds since epoch
37    pub const fn from_seconds(seconds_since_epoch: u64) -> Self {
38        Timestamp(Uint64::new(seconds_since_epoch * 1_000_000_000))
39    }
40
41    pub const fn plus_seconds(&self, addition: u64) -> Timestamp {
42        self.plus_nanos(addition * 1_000_000_000)
43    }
44
45    pub const fn plus_nanos(&self, addition: u64) -> Timestamp {
46        let nanos = Uint64::new(self.0.u64() + addition);
47        Timestamp(nanos)
48    }
49
50    pub const fn minus_seconds(&self, subtrahend: u64) -> Timestamp {
51        self.minus_nanos(subtrahend * 1_000_000_000)
52    }
53
54    pub const fn minus_nanos(&self, subtrahend: u64) -> Timestamp {
55        let nanos = Uint64::new(self.0.u64() - subtrahend);
56        Timestamp(nanos)
57    }
58
59    /// Returns nanoseconds since epoch
60    #[inline]
61    pub fn nanos(&self) -> u64 {
62        self.0.u64()
63    }
64
65    /// Returns seconds since epoch (truncate nanoseconds)
66    #[inline]
67    pub fn seconds(&self) -> u64 {
68        self.0.u64() / 1_000_000_000
69    }
70
71    /// Returns nanoseconds since the last whole second (the remainder truncated
72    /// by `seconds()`)
73    #[inline]
74    pub fn subsec_nanos(&self) -> u64 {
75        self.0.u64() % 1_000_000_000
76    }
77}
78
79impl fmt::Display for Timestamp {
80    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81        let whole = self.seconds();
82        let fractional = self.subsec_nanos();
83        write!(f, "{}.{:09}", whole, fractional)
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn timestamp_from_nanos() {
93        let t = Timestamp::from_nanos(123);
94        assert_eq!(t.0.u64(), 123);
95        let t = Timestamp::from_nanos(0);
96        assert_eq!(t.0.u64(), 0);
97    }
98
99    #[test]
100    fn timestamp_from_seconds() {
101        let t = Timestamp::from_seconds(123);
102        assert_eq!(t.0.u64(), 123_000_000_000);
103        let t = Timestamp::from_seconds(0);
104        assert_eq!(t.0.u64(), 0);
105    }
106
107    #[test]
108    fn timestamp_plus_seconds() {
109        let sum = Timestamp::from_nanos(123).plus_seconds(42);
110        assert_eq!(sum.0.u64(), 42_000_000_123);
111        let sum = Timestamp::from_nanos(123).plus_seconds(0);
112        assert_eq!(sum.0.u64(), 123);
113    }
114
115    #[test]
116    fn timestamp_plus_nanos() {
117        let sum = Timestamp::from_nanos(123).plus_nanos(3);
118        assert_eq!(sum.0.u64(), 126);
119        let sum = Timestamp::from_nanos(123).plus_nanos(0);
120        assert_eq!(sum.0.u64(), 123);
121    }
122
123    #[test]
124    fn timestamp_minus_seconds() {
125        let earlier = Timestamp::from_seconds(123).minus_seconds(0);
126        assert_eq!(earlier.0.u64(), 123_000_000_000);
127        let earlier = Timestamp::from_seconds(123).minus_seconds(3);
128        assert_eq!(earlier.0.u64(), 120_000_000_000);
129        let earlier = Timestamp::from_seconds(123).minus_seconds(123);
130        assert_eq!(earlier.0.u64(), 0);
131    }
132
133    #[test]
134    #[should_panic(expected = "attempt to subtract with overflow")]
135    fn timestamp_minus_seconds_panics_on_overflow() {
136        let _earlier = Timestamp::from_seconds(100).minus_seconds(101);
137    }
138
139    #[test]
140    fn timestamp_minus_nanos() {
141        let earlier = Timestamp::from_seconds(123).minus_nanos(0);
142        assert_eq!(earlier.0.u64(), 123_000_000_000);
143        let earlier = Timestamp::from_seconds(123).minus_nanos(3);
144        assert_eq!(earlier.0.u64(), 122_999_999_997);
145        let earlier = Timestamp::from_seconds(123).minus_nanos(123_000_000_000);
146        assert_eq!(earlier.0.u64(), 0);
147    }
148
149    #[test]
150    #[should_panic(expected = "attempt to subtract with overflow")]
151    fn timestamp_minus_nanos_panics_on_overflow() {
152        let _earlier = Timestamp::from_nanos(100).minus_nanos(101);
153    }
154
155    #[test]
156    fn timestamp_nanos() {
157        let sum = Timestamp::from_nanos(123);
158        assert_eq!(sum.nanos(), 123);
159        let sum = Timestamp::from_nanos(0);
160        assert_eq!(sum.nanos(), 0);
161        let sum = Timestamp::from_nanos(987654321000);
162        assert_eq!(sum.nanos(), 987654321000);
163    }
164
165    #[test]
166    fn timestamp_seconds() {
167        let sum = Timestamp::from_nanos(987654321000);
168        assert_eq!(sum.seconds(), 987);
169        let sum = Timestamp::from_seconds(1234567).plus_nanos(8765436);
170        assert_eq!(sum.seconds(), 1234567);
171    }
172
173    #[test]
174    fn timestamp_subsec_nanos() {
175        let sum = Timestamp::from_nanos(987654321000);
176        assert_eq!(sum.subsec_nanos(), 654321000);
177        let sum = Timestamp::from_seconds(1234567).plus_nanos(8765436);
178        assert_eq!(sum.subsec_nanos(), 8765436);
179    }
180
181    #[test]
182    fn timestamp_implements_display() {
183        let embedded = format!("Time: {}", Timestamp::from_nanos(0));
184        assert_eq!(embedded, "Time: 0.000000000");
185        let embedded = format!("Time: {}", Timestamp::from_nanos(1));
186        assert_eq!(embedded, "Time: 0.000000001");
187        let embedded = format!("Time: {}", Timestamp::from_nanos(10));
188        assert_eq!(embedded, "Time: 0.000000010");
189        let embedded = format!("Time: {}", Timestamp::from_nanos(100));
190        assert_eq!(embedded, "Time: 0.000000100");
191        let embedded = format!("Time: {}", Timestamp::from_nanos(1000));
192        assert_eq!(embedded, "Time: 0.000001000");
193        let embedded = format!("Time: {}", Timestamp::from_nanos(10000));
194        assert_eq!(embedded, "Time: 0.000010000");
195        let embedded = format!("Time: {}", Timestamp::from_nanos(100000));
196        assert_eq!(embedded, "Time: 0.000100000");
197        let embedded = format!("Time: {}", Timestamp::from_nanos(1000000));
198        assert_eq!(embedded, "Time: 0.001000000");
199        let embedded = format!("Time: {}", Timestamp::from_nanos(1000000));
200        assert_eq!(embedded, "Time: 0.001000000");
201        let embedded = format!("Time: {}", Timestamp::from_nanos(10000000));
202        assert_eq!(embedded, "Time: 0.010000000");
203        let embedded = format!("Time: {}", Timestamp::from_nanos(100000000));
204        assert_eq!(embedded, "Time: 0.100000000");
205        let embedded = format!("Time: {}", Timestamp::from_nanos(1000000000));
206        assert_eq!(embedded, "Time: 1.000000000");
207        let embedded = format!("Time: {}", Timestamp::from_nanos(10000000000));
208        assert_eq!(embedded, "Time: 10.000000000");
209        let embedded = format!("Time: {}", Timestamp::from_nanos(100000000000));
210        assert_eq!(embedded, "Time: 100.000000000");
211    }
212}