1use chrono::{DateTime, Utc};
2use serde::{self, Deserialize, Deserializer, Serialize, Serializer};
3use std::fmt;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct ThreadsTime(pub DateTime<Utc>);
11
12impl ThreadsTime {
13 pub fn new(dt: DateTime<Utc>) -> Self {
15 Self(dt)
16 }
17}
18
19impl fmt::Display for ThreadsTime {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 write!(f, "{}", self.0.to_rfc3339())
22 }
23}
24
25impl Serialize for ThreadsTime {
26 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
27 serializer.serialize_str(&self.0.to_rfc3339())
28 }
29}
30
31impl<'de> Deserialize<'de> for ThreadsTime {
32 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
33 let s = String::deserialize(deserializer)?;
34
35 if let Ok(dt) = DateTime::parse_from_str(&s, "%Y-%m-%dT%H:%M:%S%z") {
37 return Ok(ThreadsTime(dt.with_timezone(&Utc)));
38 }
39
40 if let Ok(dt) = s.parse::<DateTime<Utc>>() {
42 return Ok(ThreadsTime(dt));
43 }
44
45 if let Ok(dt) = DateTime::parse_from_rfc3339(&s) {
47 return Ok(ThreadsTime(dt.with_timezone(&Utc)));
48 }
49
50 Err(serde::de::Error::custom(format!(
51 "unable to parse timestamp: {s}"
52 )))
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn test_threads_api_format() {
62 let json = r#""2024-01-15T10:30:00+0000""#;
63 let t: ThreadsTime = serde_json::from_str(json).unwrap();
64 assert_eq!(t.0.year(), 2024);
65 assert_eq!(t.0.month(), 1);
66 assert_eq!(t.0.day(), 15);
67 }
68
69 #[test]
70 fn test_iso8601_utc() {
71 let json = r#""2024-06-01T12:00:00Z""#;
72 let t: ThreadsTime = serde_json::from_str(json).unwrap();
73 assert_eq!(t.0.year(), 2024);
74 assert_eq!(t.0.month(), 6);
75 }
76
77 #[test]
78 fn test_rfc3339() {
79 let json = r#""2024-03-20T08:15:30+05:30""#;
80 let t: ThreadsTime = serde_json::from_str(json).unwrap();
81 assert_eq!(t.0.year(), 2024);
82 }
83
84 #[test]
85 fn test_serialize_roundtrip() {
86 let json = r#""2024-01-15T10:30:00+0000""#;
87 let t: ThreadsTime = serde_json::from_str(json).unwrap();
88 let serialized = serde_json::to_string(&t).unwrap();
89 let back: ThreadsTime = serde_json::from_str(&serialized).unwrap();
91 assert_eq!(t, back);
92 }
93
94 #[test]
95 fn test_display() {
96 let json = r#""2024-01-01T00:00:00Z""#;
97 let t: ThreadsTime = serde_json::from_str(json).unwrap();
98 let s = t.to_string();
99 assert!(s.contains("2024-01-01"));
100 }
101
102 #[test]
103 fn test_invalid_timestamp() {
104 let json = r#""not-a-timestamp""#;
105 let result = serde_json::from_str::<ThreadsTime>(json);
106 assert!(result.is_err());
107 }
108
109 use chrono::Datelike;
110}