sentry_types/
utils.rs
1use std::convert::{TryFrom, TryInto};
2use std::time::{Duration, SystemTime};
3
4use time::format_description::well_known::Rfc3339;
5use time::OffsetDateTime;
6
7pub fn datetime_to_timestamp(st: &SystemTime) -> f64 {
9 match st.duration_since(SystemTime::UNIX_EPOCH) {
10 Ok(duration) => duration.as_secs_f64(),
11 Err(_) => 0.0,
12 }
13}
14
15pub fn timestamp_to_datetime(ts: f64) -> Option<SystemTime> {
16 let duration = Duration::try_from_secs_f64(ts).ok()?;
17 SystemTime::UNIX_EPOCH.checked_add(duration)
18}
19
20pub fn to_rfc3339(st: &SystemTime) -> String {
21 st.duration_since(SystemTime::UNIX_EPOCH)
22 .ok()
23 .and_then(|duration| TryFrom::try_from(duration).ok())
24 .and_then(|duration| OffsetDateTime::UNIX_EPOCH.checked_add(duration))
25 .and_then(|dt| dt.format(&Rfc3339).ok())
26 .unwrap_or_default()
27}
28
29pub mod ts_seconds_float {
30 use std::fmt;
31
32 use serde::{de, ser};
33
34 use super::*;
35
36 pub fn deserialize<'de, D>(d: D) -> Result<SystemTime, D::Error>
37 where
38 D: de::Deserializer<'de>,
39 {
40 d.deserialize_any(SecondsTimestampVisitor)
41 }
42
43 pub fn serialize<S>(st: &SystemTime, serializer: S) -> Result<S::Ok, S::Error>
44 where
45 S: ser::Serializer,
46 {
47 match st.duration_since(SystemTime::UNIX_EPOCH) {
48 Ok(duration) => {
49 if duration.subsec_nanos() == 0 {
50 serializer.serialize_u64(duration.as_secs())
51 } else {
52 serializer.serialize_f64(duration.as_secs_f64())
53 }
54 }
55 Err(_) => Err(ser::Error::custom(format!(
56 "invalid `SystemTime` instance: {st:?}"
57 ))),
58 }
59 }
60
61 struct SecondsTimestampVisitor;
62
63 impl de::Visitor<'_> for SecondsTimestampVisitor {
64 type Value = SystemTime;
65
66 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
67 write!(formatter, "a unix timestamp")
68 }
69
70 fn visit_f64<E>(self, value: f64) -> Result<SystemTime, E>
71 where
72 E: de::Error,
73 {
74 match timestamp_to_datetime(value) {
75 Some(st) => Ok(st),
76 None => Err(E::custom(format!("invalid timestamp: {value}"))),
77 }
78 }
79
80 fn visit_i64<E>(self, value: i64) -> Result<SystemTime, E>
81 where
82 E: de::Error,
83 {
84 let value = value.try_into().map_err(|e| E::custom(format!("{e}")))?;
85 let duration = Duration::from_secs(value);
86 match SystemTime::UNIX_EPOCH.checked_add(duration) {
87 Some(st) => Ok(st),
88 None => Err(E::custom(format!("invalid timestamp: {value}"))),
89 }
90 }
91
92 fn visit_u64<E>(self, value: u64) -> Result<SystemTime, E>
93 where
94 E: de::Error,
95 {
96 let duration = Duration::from_secs(value);
97 match SystemTime::UNIX_EPOCH.checked_add(duration) {
98 Some(st) => Ok(st),
99 None => Err(E::custom(format!("invalid timestamp: {value}"))),
100 }
101 }
102
103 fn visit_str<E>(self, value: &str) -> Result<SystemTime, E>
104 where
105 E: de::Error,
106 {
107 let rfc3339_deser = super::ts_rfc3339::Rfc3339Deserializer;
108 rfc3339_deser.visit_str(value)
109 }
110 }
111}
112
113pub mod ts_rfc3339 {
114 use std::fmt;
115
116 use serde::{de, ser};
117
118 use super::*;
119
120 pub fn deserialize<'de, D>(d: D) -> Result<SystemTime, D::Error>
121 where
122 D: de::Deserializer<'de>,
123 {
124 d.deserialize_any(Rfc3339Deserializer)
125 }
126
127 pub fn serialize<S>(st: &SystemTime, serializer: S) -> Result<S::Ok, S::Error>
128 where
129 S: ser::Serializer,
130 {
131 match st
132 .duration_since(SystemTime::UNIX_EPOCH)
133 .ok()
134 .and_then(|duration| TryFrom::try_from(duration).ok())
135 .and_then(|duration| OffsetDateTime::UNIX_EPOCH.checked_add(duration))
136 .and_then(|dt| dt.format(&Rfc3339).ok())
137 {
138 Some(formatted) => serializer.serialize_str(&formatted),
139 None => Err(ser::Error::custom(format!(
140 "invalid `SystemTime` instance: {st:?}"
141 ))),
142 }
143 }
144
145 pub(super) struct Rfc3339Deserializer;
146
147 impl de::Visitor<'_> for Rfc3339Deserializer {
148 type Value = SystemTime;
149
150 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
151 write!(formatter, "an RFC3339 timestamp")
152 }
153
154 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
155 where
156 E: de::Error,
157 {
158 let dt = OffsetDateTime::parse(v, &Rfc3339).map_err(|e| E::custom(format!("{e}")))?;
159 let secs = u64::try_from(dt.unix_timestamp()).map_err(|e| E::custom(format!("{e}")))?;
160 let nanos = dt.nanosecond();
161 let duration = Duration::new(secs, nanos);
162 SystemTime::UNIX_EPOCH
163 .checked_add(duration)
164 .ok_or_else(|| E::custom("invalid timestamp"))
165 }
166 }
167}
168
169pub mod ts_rfc3339_opt {
170 use serde::{de, ser};
171
172 use super::*;
173
174 pub fn deserialize<'de, D>(d: D) -> Result<Option<SystemTime>, D::Error>
175 where
176 D: de::Deserializer<'de>,
177 {
178 ts_rfc3339::deserialize(d).map(Some)
179 }
180
181 pub fn serialize<S>(st: &Option<SystemTime>, serializer: S) -> Result<S::Ok, S::Error>
182 where
183 S: ser::Serializer,
184 {
185 match st {
186 Some(st) => ts_rfc3339::serialize(st, serializer),
187 None => serializer.serialize_none(),
188 }
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::timestamp_to_datetime;
195
196 #[test]
197 fn test_timestamp_to_datetime() {
198 assert!(timestamp_to_datetime(-10000.0).is_none());
199 assert!(timestamp_to_datetime(f64::INFINITY).is_none());
200 assert!(timestamp_to_datetime(f64::MAX).is_none());
201 assert!(timestamp_to_datetime(123123123.0).is_some());
202 }
203}