sync15/
server_timestamp.rs1use std::time::{Duration, SystemTime, UNIX_EPOCH};
5
6const MAX_FLOAT_MILLISECONDS: f64 = ((1u64 << f64::MANTISSA_DIGITS) - 1) as f64;
9
10#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Default)]
13pub struct ServerTimestamp(pub i64);
14
15impl ServerTimestamp {
16 pub fn from_float_seconds(ts: f64) -> Self {
17 let rf = (ts * 1000.0).round();
18 if !(0.0..=MAX_FLOAT_MILLISECONDS).contains(&rf) {
19 error_support::report_error!("sync15-illegal-timestamp", "Illegal timestamp: {}", ts);
20 ServerTimestamp(0)
21 } else {
22 ServerTimestamp(rf as i64)
23 }
24 }
25
26 pub fn from_millis(ts: i64) -> Self {
27 debug_assert!(ts >= 0, "Bad timestamp: {}", ts);
29 if ts >= 0 {
30 Self(ts)
31 } else {
32 error_support::report_error!(
33 "sync15-illegal-timestamp",
34 "Illegal timestamp, substituting 0: {}",
35 ts
36 );
37 Self(0)
38 }
39 }
40}
41
42impl std::str::FromStr for ServerTimestamp {
44 type Err = std::num::ParseFloatError;
45 fn from_str(s: &str) -> Result<Self, Self::Err> {
46 let val = f64::from_str(s)?;
47 Ok(Self::from_float_seconds(val))
48 }
49}
50
51impl std::fmt::Display for ServerTimestamp {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 write!(f, "{}", self.0 as f64 / 1000.0)
54 }
55}
56
57impl ServerTimestamp {
58 pub const EPOCH: ServerTimestamp = ServerTimestamp(0);
59
60 #[inline]
63 pub fn duration_since(self, other: ServerTimestamp) -> Option<Duration> {
64 let delta = self.0 - other.0;
65 if delta < 0 {
66 None
67 } else {
68 Some(Duration::from_millis(delta as u64))
69 }
70 }
71
72 #[inline]
74 pub fn as_millis(self) -> i64 {
75 self.0
76 }
77}
78
79impl serde::ser::Serialize for ServerTimestamp {
80 fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
81 serializer.serialize_f64(self.0 as f64 / 1000.0)
82 }
83}
84
85impl<'de> serde::de::Deserialize<'de> for ServerTimestamp {
86 fn deserialize<D: serde::de::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
87 f64::deserialize(d).map(Self::from_float_seconds)
88 }
89}
90
91impl TryFrom<SystemTime> for ServerTimestamp {
95 type Error = std::time::SystemTimeError;
96
97 fn try_from(value: SystemTime) -> Result<Self, Self::Error> {
98 Ok(Self(value.duration_since(UNIX_EPOCH)?.as_millis() as i64))
99 }
100}
101
102#[cfg(test)]
103mod test {
104 use super::*;
105
106 #[test]
107 fn test_server_timestamp() {
108 let t0 = ServerTimestamp(10_300_150);
109 let t1 = ServerTimestamp(10_100_050);
110 assert!(t1.duration_since(t0).is_none());
111 assert!(t0.duration_since(t1).is_some());
112 let dur = t0.duration_since(t1).unwrap();
113 assert_eq!(dur.as_secs(), 200);
114 assert_eq!(dur.subsec_nanos(), 100_000_000);
115 }
116
117 #[test]
118 fn test_serde() {
119 let ts = ServerTimestamp(123_456);
120
121 let ser = serde_json::to_string(&ts).unwrap();
123 assert_eq!("123.456".to_string(), ser);
124
125 let ts: ServerTimestamp = serde_json::from_str(&ser).unwrap();
127 assert_eq!(ServerTimestamp(123_456), ts);
128
129 let ts: ServerTimestamp = serde_json::from_str("123").unwrap();
131 assert_eq!(ServerTimestamp(123_000), ts);
132
133 let ts: ServerTimestamp = serde_json::from_str("-123").unwrap();
135 assert_eq!(ServerTimestamp(0), ts);
136 }
137}