Skip to repository content167 lines · 4.9 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T02:19:32.188Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
timestamp.rs
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(×tamp).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(×tamp).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(×tamp).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