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