1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
3
4#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
5pub struct UnixMicroseconds(DateTime<Utc>);
6
7impl From<DateTime<Utc>> for UnixMicroseconds {
8 #[inline]
9 fn from(value: DateTime<Utc>) -> Self {
10 Self(value)
11 }
12}
13
14impl From<UnixMicroseconds> for DateTime<Utc> {
15 #[inline]
16 fn from(value: UnixMicroseconds) -> Self {
17 value.0
18 }
19}
20
21impl TryFrom<i64> for UnixMicroseconds {
22 type Error = TryFromTimestampError;
23
24 #[inline]
25 fn try_from(value: i64) -> Result<Self, Self::Error> {
26 Ok(Self(DateTime::from_timestamp_micros(value).ok_or_else(
27 || TryFromTimestampError::Range(value.into()),
28 )?))
29 }
30}
31
32impl Serialize for UnixMicroseconds {
33 #[inline]
34 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
35 serializer.serialize_i64(self.0.timestamp_micros())
36 }
37}
38
39impl<'de> Deserialize<'de> for UnixMicroseconds {
40 #[inline]
41 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
42 use de::Error;
43 let timestamp = NumericTimestamp::deserialize(deserializer)?;
44 let micros = timestamp.try_into().map_err(D::Error::custom)?;
45 DateTime::from_timestamp_micros(micros)
46 .map(Self)
47 .ok_or_else(|| D::Error::custom(TryFromTimestampError::Range(micros.into())))
48 }
49}
50
51#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
52pub struct UnixMilliseconds(DateTime<Utc>);
53
54impl From<DateTime<Utc>> for UnixMilliseconds {
55 #[inline]
56 fn from(value: DateTime<Utc>) -> Self {
57 Self(value)
58 }
59}
60
61impl From<UnixMilliseconds> for DateTime<Utc> {
62 #[inline]
63 fn from(value: UnixMilliseconds) -> Self {
64 value.0
65 }
66}
67
68impl TryFrom<i64> for UnixMilliseconds {
69 type Error = TryFromTimestampError;
70
71 #[inline]
72 fn try_from(value: i64) -> Result<Self, Self::Error> {
73 Ok(Self(DateTime::from_timestamp_millis(value).ok_or_else(
74 || TryFromTimestampError::Range(value.into()),
75 )?))
76 }
77}
78
79impl Serialize for UnixMilliseconds {
80 #[inline]
81 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
82 serializer.serialize_i64(self.0.timestamp_millis())
83 }
84}
85
86impl<'de> Deserialize<'de> for UnixMilliseconds {
87 #[inline]
88 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
89 use de::Error;
90 let timestamp = NumericTimestamp::deserialize(deserializer)?;
91 let millis = timestamp.try_into().map_err(D::Error::custom)?;
92 DateTime::from_timestamp_millis(millis)
93 .map(Self)
94 .ok_or_else(|| D::Error::custom(TryFromTimestampError::Range(millis.into())))
95 }
96}
97
98#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
99pub struct UnixNanoseconds(DateTime<Utc>);
100
101impl From<DateTime<Utc>> for UnixNanoseconds {
102 #[inline]
103 fn from(value: DateTime<Utc>) -> Self {
104 Self(value)
105 }
106}
107
108impl From<UnixNanoseconds> for DateTime<Utc> {
109 #[inline]
110 fn from(value: UnixNanoseconds) -> Self {
111 value.0
112 }
113}
114
115impl From<i64> for UnixNanoseconds {
116 #[inline]
117 fn from(value: i64) -> Self {
118 Self(DateTime::from_timestamp_nanos(value))
119 }
120}
121
122impl Serialize for UnixNanoseconds {
123 #[inline]
124 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
125 serializer.serialize_i64(self.0.timestamp_nanos_opt().unwrap_or(i64::MAX))
126 }
127}
128
129impl<'de> Deserialize<'de> for UnixNanoseconds {
130 #[inline]
131 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
132 use de::Error;
133 let timestamp = NumericTimestamp::deserialize(deserializer)?;
134 let nanos = timestamp.try_into().map_err(D::Error::custom)?;
135 Ok(Self(DateTime::from_timestamp_nanos(nanos)))
136 }
137}
138
139#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
140pub struct UnixSeconds(DateTime<Utc>);
141
142impl From<DateTime<Utc>> for UnixSeconds {
143 #[inline]
144 fn from(value: DateTime<Utc>) -> Self {
145 Self(value)
146 }
147}
148
149impl From<UnixSeconds> for DateTime<Utc> {
150 #[inline]
151 fn from(value: UnixSeconds) -> Self {
152 value.0
153 }
154}
155
156impl TryFrom<i64> for UnixSeconds {
157 type Error = TryFromTimestampError;
158
159 #[inline]
160 fn try_from(value: i64) -> Result<Self, Self::Error> {
161 Ok(Self(DateTime::from_timestamp_secs(value).ok_or_else(
162 || TryFromTimestampError::Range(value.into()),
163 )?))
164 }
165}
166
167impl Serialize for UnixSeconds {
168 #[inline]
169 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
170 serializer.serialize_i64(self.0.timestamp())
171 }
172}
173
174impl<'de> Deserialize<'de> for UnixSeconds {
175 #[inline]
176 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
177 use de::Error;
178 let timestamp = NumericTimestamp::deserialize(deserializer)?;
179 let secs = timestamp.try_into().map_err(D::Error::custom)?;
180 DateTime::from_timestamp_secs(secs)
181 .map(Self)
182 .ok_or_else(|| D::Error::custom(TryFromTimestampError::Range(secs.into())))
183 }
184}
185
186#[derive(Deserialize)]
187#[serde(untagged)]
188enum NumericTimestamp<'a> {
189 I64(i64),
190 U64(u64),
191 Str(&'a str),
192}
193
194impl TryFrom<NumericTimestamp<'_>> for i64 {
195 type Error = TryFromTimestampError;
196
197 fn try_from(value: NumericTimestamp<'_>) -> Result<Self, Self::Error> {
198 Ok(match value {
199 NumericTimestamp::I64(n) => n,
200 NumericTimestamp::U64(n) => {
201 Self::try_from(n).map_err(|_| TryFromTimestampError::Range(n.into()))?
202 }
203 NumericTimestamp::Str(s) => s
204 .parse()
205 .map_err(|_| TryFromTimestampError::Str(s.to_owned()))?,
206 })
207 }
208}
209
210#[derive(Debug, thiserror::Error)]
211pub enum TryFromTimestampError {
212 #[error("timestamp `{0}` out of range for `DateTime<Utc>`")]
213 Range(i128),
214 #[error("can't convert `{0}` to timestamp")]
215 Str(String),
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
225 fn test_unix_seconds_deserialize_from_number() {
226 let json = "1609459200";
227 let result: UnixSeconds = serde_json::from_str(json).unwrap();
228 let dt: DateTime<Utc> = result.into();
229 assert_eq!(dt.timestamp(), 1609459200);
230 }
231
232 #[test]
233 fn test_unix_seconds_deserialize_from_string() {
234 let json = r#""1609459200""#;
235 let result: UnixSeconds = serde_json::from_str(json).unwrap();
236 let dt: DateTime<Utc> = result.into();
237 assert_eq!(dt.timestamp(), 1609459200);
238 }
239
240 #[test]
241 fn test_unix_seconds_serialize() {
242 let dt = DateTime::from_timestamp_secs(1609459200).unwrap();
243 let ts = UnixSeconds::from(dt);
244 let json = serde_json::to_string(&ts).unwrap();
245 assert_eq!(json, "1609459200");
246 }
247
248 #[test]
249 fn test_unix_seconds_invalid_string() {
250 let json = r#""not-a-number""#;
251 let result: Result<UnixSeconds, _> = serde_json::from_str(json);
252 assert!(result.is_err());
253 }
254
255 #[test]
258 fn test_unix_milliseconds_deserialize_from_number() {
259 let json = "1609459200123";
260 let result: UnixMilliseconds = serde_json::from_str(json).unwrap();
261 let dt: DateTime<Utc> = result.into();
262 assert_eq!(dt.timestamp_millis(), 1609459200123);
263 }
264
265 #[test]
266 fn test_unix_milliseconds_deserialize_from_string() {
267 let json = r#""1609459200123""#;
268 let result: UnixMilliseconds = serde_json::from_str(json).unwrap();
269 let dt: DateTime<Utc> = result.into();
270 assert_eq!(dt.timestamp_millis(), 1609459200123);
271 }
272
273 #[test]
274 fn test_unix_milliseconds_serialize() {
275 let dt = DateTime::from_timestamp_millis(1609459200123).unwrap();
276 let ts = UnixMilliseconds::from(dt);
277 let json = serde_json::to_string(&ts).unwrap();
278 assert_eq!(json, "1609459200123");
279 }
280
281 #[test]
282 fn test_unix_milliseconds_invalid_string() {
283 let json = r#""not-a-number""#;
284 let result: Result<UnixMilliseconds, _> = serde_json::from_str(json);
285 assert!(result.is_err());
286 }
287
288 #[test]
291 fn test_unix_microseconds_deserialize_from_number() {
292 let json = "1609459200123456";
293 let result: UnixMicroseconds = serde_json::from_str(json).unwrap();
294 let dt: DateTime<Utc> = result.into();
295 assert_eq!(dt.timestamp_micros(), 1609459200123456);
296 }
297
298 #[test]
299 fn test_unix_microseconds_deserialize_from_string() {
300 let json = r#""1609459200123456""#;
301 let result: UnixMicroseconds = serde_json::from_str(json).unwrap();
302 let dt: DateTime<Utc> = result.into();
303 assert_eq!(dt.timestamp_micros(), 1609459200123456);
304 }
305
306 #[test]
307 fn test_unix_microseconds_serialize() {
308 let dt = DateTime::from_timestamp_micros(1609459200123456).unwrap();
309 let ts = UnixMicroseconds::from(dt);
310 let json = serde_json::to_string(&ts).unwrap();
311 assert_eq!(json, "1609459200123456");
312 }
313
314 #[test]
315 fn test_unix_microseconds_invalid_string() {
316 let json = r#""not-a-number""#;
317 let result: Result<UnixMicroseconds, _> = serde_json::from_str(json);
318 assert!(result.is_err());
319 }
320
321 #[test]
324 fn test_unix_nanoseconds_deserialize_from_number() {
325 let json = "1609459200123456789";
326 let result: UnixNanoseconds = serde_json::from_str(json).unwrap();
327 let dt: DateTime<Utc> = result.into();
328 assert_eq!(dt.timestamp_nanos_opt(), Some(1609459200123456789));
329 }
330
331 #[test]
332 fn test_unix_nanoseconds_deserialize_from_string() {
333 let json = r#""1609459200123456789""#;
334 let result: UnixNanoseconds = serde_json::from_str(json).unwrap();
335 let dt: DateTime<Utc> = result.into();
336 assert_eq!(dt.timestamp_nanos_opt(), Some(1609459200123456789));
337 }
338
339 #[test]
340 fn test_unix_nanoseconds_serialize() {
341 let dt = DateTime::from_timestamp_nanos(1609459200123456789);
342 let ts = UnixNanoseconds::from(dt);
343 let json = serde_json::to_string(&ts).unwrap();
344 assert_eq!(json, "1609459200123456789");
345 }
346
347 #[test]
348 fn test_unix_nanoseconds_invalid_string() {
349 let json = r#""not-a-number""#;
350 let result: Result<UnixNanoseconds, _> = serde_json::from_str(json);
351 assert!(result.is_err());
352 }
353}