Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:19:32.188Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

timestamp.rs

167 lines · 4.9 KB · rust
1use chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc};
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3
4/// A timestamp with a serialized representation in RFC 3339 format.
5#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
6pub struct Timestamp(pub DateTime<Utc>);
7
8impl Timestamp {
9    pub fn new(datetime: DateTime<Utc>) -> Self {
10        Self(datetime)
11    }
12}
13
14impl From<DateTime<Utc>> for Timestamp {
15    fn from(value: DateTime<Utc>) -> Self {
16        Self(value)
17    }
18}
19
20impl From<NaiveDateTime> for Timestamp {
21    fn from(value: NaiveDateTime) -> Self {
22        Self(value.and_utc())
23    }
24}
25
26impl Serialize for Timestamp {
27    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
28    where
29        S: Serializer,
30    {
31        let rfc3339_string = self.0.to_rfc3339_opts(SecondsFormat::Millis, true);
32        serializer.serialize_str(&rfc3339_string)
33    }
34}
35
36impl<'de> Deserialize<'de> for Timestamp {
37    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
38    where
39        D: Deserializer<'de>,
40    {
41        let value = String::deserialize(deserializer)?;
42        let datetime = DateTime::parse_from_rfc3339(&value)
43            .map_err(serde::de::Error::custom)?
44            .to_utc();
45        Ok(Self(datetime))
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use chrono::NaiveDate;
52    use pretty_assertions::assert_eq;
53
54    use super::*;
55
56    #[test]
57    fn test_timestamp_serialization() {
58        let datetime = DateTime::parse_from_rfc3339("2023-12-25T14:30:45.123Z")
59            .unwrap()
60            .to_utc();
61        let timestamp = Timestamp::new(datetime);
62
63        let json = serde_json::to_string(&timestamp).unwrap();
64        assert_eq!(json, "\"2023-12-25T14:30:45.123Z\"");
65    }
66
67    #[test]
68    fn test_timestamp_deserialization() {
69        let json = "\"2023-12-25T14:30:45.123Z\"";
70        let timestamp: Timestamp = serde_json::from_str(json).unwrap();
71
72        let expected = DateTime::parse_from_rfc3339("2023-12-25T14:30:45.123Z")
73            .unwrap()
74            .to_utc();
75
76        assert_eq!(timestamp.0, expected);
77    }
78
79    #[test]
80    fn test_timestamp_roundtrip() {
81        let original = DateTime::parse_from_rfc3339("2023-12-25T14:30:45.123Z")
82            .unwrap()
83            .to_utc();
84
85        let timestamp = Timestamp::new(original);
86        let json = serde_json::to_string(&timestamp).unwrap();
87        let deserialized: Timestamp = serde_json::from_str(&json).unwrap();
88
89        assert_eq!(deserialized.0, original);
90    }
91
92    #[test]
93    fn test_timestamp_from_datetime_utc() {
94        let datetime = DateTime::parse_from_rfc3339("2023-12-25T14:30:45.123Z")
95            .unwrap()
96            .to_utc();
97
98        let timestamp = Timestamp::from(datetime);
99        assert_eq!(timestamp.0, datetime);
100    }
101
102    #[test]
103    fn test_timestamp_from_naive_datetime() {
104        let naive_dt = NaiveDate::from_ymd_opt(2023, 12, 25)
105            .unwrap()
106            .and_hms_milli_opt(14, 30, 45, 123)
107            .unwrap();
108
109        let timestamp = Timestamp::from(naive_dt);
110        let expected = naive_dt.and_utc();
111
112        assert_eq!(timestamp.0, expected);
113    }
114
115    #[test]
116    fn test_timestamp_serialization_with_microseconds() {
117        // Test that microseconds are truncated to milliseconds
118        let datetime = NaiveDate::from_ymd_opt(2023, 12, 25)
119            .unwrap()
120            .and_hms_micro_opt(14, 30, 45, 123456)
121            .unwrap()
122            .and_utc();
123
124        let timestamp = Timestamp::new(datetime);
125        let json = serde_json::to_string(&timestamp).unwrap();
126
127        // Should be truncated to milliseconds
128        assert_eq!(json, "\"2023-12-25T14:30:45.123Z\"");
129    }
130
131    #[test]
132    fn test_timestamp_deserialization_without_milliseconds() {
133        let json = "\"2023-12-25T14:30:45Z\"";
134        let timestamp: Timestamp = serde_json::from_str(json).unwrap();
135
136        let expected = NaiveDate::from_ymd_opt(2023, 12, 25)
137            .unwrap()
138            .and_hms_opt(14, 30, 45)
139            .unwrap()
140            .and_utc();
141
142        assert_eq!(timestamp.0, expected);
143    }
144
145    #[test]
146    fn test_timestamp_deserialization_with_timezone() {
147        let json = "\"2023-12-25T14:30:45.123+05:30\"";
148        let timestamp: Timestamp = serde_json::from_str(json).unwrap();
149
150        // Should be converted to UTC
151        let expected = NaiveDate::from_ymd_opt(2023, 12, 25)
152            .unwrap()
153            .and_hms_milli_opt(9, 0, 45, 123) // 14:30:45 + 5:30 = 20:00:45, but we want UTC so subtract 5:30
154            .unwrap()
155            .and_utc();
156
157        assert_eq!(timestamp.0, expected);
158    }
159
160    #[test]
161    fn test_timestamp_deserialization_with_invalid_format() {
162        let json = "\"invalid-date\"";
163        let result: Result<Timestamp, _> = serde_json::from_str(json);
164        assert!(result.is_err());
165    }
166}
167
Served at tenant.openagents/omega Member data and write actions are omitted.