spacetimedb_sats/
time_duration.rs1use crate::timestamp::MICROSECONDS_PER_SECOND;
2use crate::{de::Deserialize, impl_st, ser::Serialize, AlgebraicType};
3use std::fmt;
4use std::time::Duration;
5
6#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, Debug)]
7#[sats(crate = crate)]
8pub struct TimeDuration {
17 __time_duration_micros__: i64,
18}
19
20impl_st!([] TimeDuration, AlgebraicType::time_duration());
21
22impl TimeDuration {
23 pub const ZERO: TimeDuration = TimeDuration {
24 __time_duration_micros__: 0,
25 };
26
27 pub fn to_micros(self) -> i64 {
29 self.__time_duration_micros__
30 }
31
32 pub fn from_micros(micros: i64) -> Self {
37 Self {
38 __time_duration_micros__: micros,
39 }
40 }
41
42 pub fn to_duration(self) -> Result<Duration, Duration> {
44 let micros = self.to_micros();
45 if micros >= 0 {
46 Ok(Duration::from_micros(micros as u64))
47 } else {
48 Err(Duration::from_micros((-micros) as u64))
49 }
50 }
51
52 pub fn to_duration_abs(self) -> Duration {
56 match self.to_duration() {
57 Ok(dur) | Err(dur) => dur,
58 }
59 }
60
61 pub fn from_duration(duration: Duration) -> Self {
65 Self::from_micros(
66 duration
67 .as_micros()
68 .try_into()
69 .expect("Duration overflows i64 microseconds"),
70 )
71 }
72}
73
74impl From<Duration> for TimeDuration {
75 fn from(d: Duration) -> TimeDuration {
76 TimeDuration::from_duration(d)
77 }
78}
79
80impl TryFrom<TimeDuration> for Duration {
81 type Error = Duration;
82 fn try_from(d: TimeDuration) -> Result<Duration, Duration> {
84 d.to_duration()
85 }
86}
87
88impl fmt::Display for TimeDuration {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 let micros = self.to_micros();
91 let sign = if micros < 0 { "-" } else { "+" };
92 let pos = micros.abs();
93 let secs = pos / MICROSECONDS_PER_SECOND;
94 let micros_remaining = pos % MICROSECONDS_PER_SECOND;
95 write!(f, "{sign}{secs}.{micros_remaining:06}")
96 }
97}
98
99#[cfg(test)]
100mod test {
101 use super::*;
102 use crate::GroundSpacetimeType;
103 use proptest::prelude::*;
104 use std::time::SystemTime;
105
106 #[test]
107 fn timestamp_type_matches() {
108 assert_eq!(AlgebraicType::time_duration(), TimeDuration::get_type());
109 assert!(TimeDuration::get_type().is_time_duration());
110 assert!(TimeDuration::get_type().is_special());
111 }
112
113 #[test]
114 fn round_trip_duration_through_time_duration() {
115 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
116 let rounded = Duration::from_micros(now.as_micros() as _);
117 let time_duration = TimeDuration::from_duration(rounded);
118 let now_prime = time_duration.to_duration().unwrap();
119 assert_eq!(rounded, now_prime);
120 }
121
122 proptest! {
123 #[test]
124 fn round_trip_time_duration_through_systemtime(micros in any::<i64>().prop_map(|n| n.abs())) {
125 let time_duration = TimeDuration::from_micros(micros);
126 let duration = time_duration.to_duration().unwrap();
127 let time_duration_prime = TimeDuration::from_duration(duration);
128 prop_assert_eq!(time_duration_prime, time_duration);
129 prop_assert_eq!(time_duration_prime.to_micros(), micros);
130 }
131 }
132}