desert_core/features/
chrono.rs1use crate::binary_input::BinaryInput;
2use crate::binary_output::BinaryOutput;
3use crate::deserializer::DeserializationContext;
4use crate::serializer::SerializationContext;
5use crate::{BinaryDeserializer, BinarySerializer, Error, Result};
6use bigdecimal::FromPrimitive;
7use chrono::{
8 DateTime, FixedOffset, Local, Month, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Timelike,
9 Utc, Weekday,
10};
11use chrono_tz::{OffsetName, Tz};
12use std::str::FromStr;
13
14impl BinarySerializer for Weekday {
15 fn serialize<Output: BinaryOutput>(
16 &self,
17 context: &mut SerializationContext<Output>,
18 ) -> Result<()> {
19 (self.number_from_monday() as i8).serialize(context)
20 }
21}
22
23impl BinaryDeserializer for Weekday {
24 fn deserialize(context: &mut DeserializationContext<'_>) -> Result<Self> {
25 Weekday::from_i8(i8::deserialize(context)? - 1).ok_or_else(|| {
26 Error::DeserializationFailure("Failed to deserialize Weekday".to_string())
27 })
28 }
29}
30
31impl BinarySerializer for Month {
32 fn serialize<Output: BinaryOutput>(
33 &self,
34 context: &mut SerializationContext<Output>,
35 ) -> Result<()> {
36 (self.number_from_month() as i8).serialize(context)
37 }
38}
39
40impl BinaryDeserializer for Month {
41 fn deserialize(context: &mut DeserializationContext<'_>) -> Result<Self> {
42 Month::from_i8(i8::deserialize(context)?)
43 .ok_or_else(|| Error::DeserializationFailure("Failed to deserialize Month".to_string()))
44 }
45}
46
47impl BinarySerializer for FixedOffset {
48 fn serialize<Output: BinaryOutput>(
49 &self,
50 context: &mut SerializationContext<Output>,
51 ) -> Result<()> {
52 context.write_u8(0);
53 context.write_var_i32(self.local_minus_utc());
54 Ok(())
55 }
56}
57
58impl BinaryDeserializer for FixedOffset {
59 fn deserialize(context: &mut DeserializationContext<'_>) -> Result<Self> {
60 let typ = context.read_u8()?;
61 if typ != 0 {
62 Err(Error::DeserializationFailure(format!(
63 "Failed to deserialize FixedOffset: Invalid type {}",
64 typ
65 )))?
66 } else {
67 let offset = context.read_var_i32()?;
68 FixedOffset::east_opt(offset).ok_or_else(|| {
69 Error::DeserializationFailure(format!(
70 "Failed to deserialize FixedOffset: Invalid offset {}",
71 offset
72 ))
73 })
74 }
75 }
76}
77
78impl BinarySerializer for Tz {
79 fn serialize<Output: BinaryOutput>(
80 &self,
81 context: &mut SerializationContext<Output>,
82 ) -> Result<()> {
83 context.write_u8(1);
84 self.name().serialize(context)
85 }
86}
87
88impl BinaryDeserializer for Tz {
89 fn deserialize(context: &mut DeserializationContext<'_>) -> Result<Self> {
90 let typ = context.read_u8()?;
91 if typ != 1 {
92 Err(Error::DeserializationFailure(format!(
93 "Failed to deserialize Tz: Invalid type {}",
94 typ
95 )))?
96 } else {
97 let name = String::deserialize(context)?;
98 Tz::from_str(&name).map_err(|err| {
99 Error::DeserializationFailure(format!("Failed to deserialize Tz: {}", err))
100 })
101 }
102 }
103}
104
105impl BinarySerializer for DateTime<Utc> {
106 fn serialize<Output: BinaryOutput>(
107 &self,
108 context: &mut SerializationContext<Output>,
109 ) -> Result<()> {
110 context.write_i64(self.timestamp());
111 context.write_u32(self.timestamp_subsec_nanos());
112 Ok(())
113 }
114}
115
116impl BinaryDeserializer for DateTime<Utc> {
117 fn deserialize(context: &mut DeserializationContext<'_>) -> Result<Self> {
118 let seconds = context.read_i64()?;
119 let nanos = context.read_u32()?;
120 DateTime::<Utc>::from_timestamp(seconds, nanos).ok_or_else(|| {
121 Error::DeserializationFailure(format!(
122 "Failed to deserialize DateTime<Utc>: Invalid timestamp {} {}",
123 seconds, nanos
124 ))
125 })
126 }
127}
128
129impl BinarySerializer for NaiveDate {
130 fn serialize<Output: BinaryOutput>(
131 &self,
132 context: &mut SerializationContext<Output>,
133 ) -> Result<()> {
134 use chrono::Datelike;
135
136 context.write_var_u32(self.year() as u32);
137 context.write_u8(self.month() as u8);
138 context.write_u8(self.day() as u8);
139 Ok(())
140 }
141}
142
143impl BinaryDeserializer for NaiveDate {
144 fn deserialize(context: &mut DeserializationContext<'_>) -> Result<Self> {
145 let year = context.read_var_u32()?;
146 let month = context.read_u8()?;
147 let day = context.read_u8()?;
148 NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32).ok_or_else(|| {
149 Error::DeserializationFailure(format!(
150 "Failed to deserialize NaiveDate: Invalid date {} {} {}",
151 year, month, day
152 ))
153 })
154 }
155}
156
157impl BinarySerializer for NaiveTime {
158 fn serialize<Output: BinaryOutput>(
159 &self,
160 context: &mut SerializationContext<Output>,
161 ) -> Result<()> {
162 context.write_u8(self.hour() as u8);
163 context.write_u8(self.minute() as u8);
164 context.write_u8(self.second() as u8);
165 context.write_var_u32(self.nanosecond());
166 Ok(())
167 }
168}
169
170impl BinaryDeserializer for NaiveTime {
171 fn deserialize(context: &mut DeserializationContext<'_>) -> Result<Self> {
172 let hour = context.read_u8()?;
173 let minute = context.read_u8()?;
174 let second = context.read_u8()?;
175 let nanosecond = context.read_var_u32()?;
176 NaiveTime::from_hms_nano_opt(hour as u32, minute as u32, second as u32, nanosecond)
177 .ok_or_else(|| {
178 Error::DeserializationFailure(format!(
179 "Failed to deserialize NaiveTime: Invalid time {} {} {} {}",
180 hour, minute, second, nanosecond
181 ))
182 })
183 }
184}
185
186impl BinarySerializer for NaiveDateTime {
187 fn serialize<Output: BinaryOutput>(
188 &self,
189 context: &mut SerializationContext<Output>,
190 ) -> Result<()> {
191 self.date().serialize(context)?;
192 self.time().serialize(context)?;
193 Ok(())
194 }
195}
196
197impl BinaryDeserializer for NaiveDateTime {
198 fn deserialize(context: &mut DeserializationContext<'_>) -> Result<Self> {
199 let date = NaiveDate::deserialize(context)?;
200 let time = NaiveTime::deserialize(context)?;
201 Ok(NaiveDateTime::new(date, time))
202 }
203}
204
205impl BinarySerializer for DateTime<Local> {
206 fn serialize<Output: BinaryOutput>(
207 &self,
208 context: &mut SerializationContext<Output>,
209 ) -> Result<()> {
210 self.date_naive().serialize(context)?;
211 self.time().serialize(context)?;
212 Ok(())
213 }
214}
215
216impl BinaryDeserializer for DateTime<Local> {
217 fn deserialize(context: &mut DeserializationContext<'_>) -> Result<Self> {
218 let date = NaiveDate::deserialize(context)?;
219 let time = NaiveTime::deserialize(context)?;
220 let naive = NaiveDateTime::new(date, time);
221 Local.from_local_datetime(&naive).single().ok_or_else(|| {
222 Error::DeserializationFailure(format!("Failed to deserialize DateTime<Local>: {naive}"))
223 })
224 }
225}
226
227impl BinarySerializer for DateTime<FixedOffset> {
228 fn serialize<Output: BinaryOutput>(
229 &self,
230 context: &mut SerializationContext<Output>,
231 ) -> Result<()> {
232 self.naive_local().serialize(context)?;
233 self.offset().serialize(context)?;
234 Ok(())
235 }
236}
237
238impl BinaryDeserializer for DateTime<FixedOffset> {
239 fn deserialize(context: &mut DeserializationContext<'_>) -> Result<Self> {
240 let naive = NaiveDateTime::deserialize(context)?;
241 let offset = FixedOffset::deserialize(context)?;
242 offset.from_local_datetime(&naive).single().ok_or_else(|| {
243 Error::DeserializationFailure(format!(
244 "Failed to deserialize DateTime<FixedOffset>: {naive}"
245 ))
246 })
247 }
248}
249
250impl BinarySerializer for DateTime<Tz> {
251 fn serialize<Output: BinaryOutput>(
252 &self,
253 context: &mut SerializationContext<Output>,
254 ) -> Result<()> {
255 self.naive_utc().serialize(context)?;
256 Tz::from_str(self.offset().tz_id())?.serialize(context)?;
257 Ok(())
258 }
259}
260
261impl BinaryDeserializer for DateTime<Tz> {
262 fn deserialize(context: &mut DeserializationContext<'_>) -> Result<Self> {
263 let naive = NaiveDateTime::deserialize(context)?;
264 let tz = Tz::deserialize(context)?;
265 Ok(tz.from_utc_datetime(&naive))
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use crate::tests::roundtrip;
272 use chrono::{
273 DateTime, FixedOffset, Local, Month, NaiveDate, NaiveDateTime, TimeZone, Utc, Weekday,
274 };
275 use chrono_tz::Tz;
276 use proptest::prelude::*;
277 use proptest_arbitrary_interop::arb;
278 use test_r::test;
279
280 fn datetime_tz_strategy() -> impl Strategy<Value = DateTime<Tz>> {
281 (arb::<NaiveDateTime>(), arb::<Tz>())
282 .prop_map(|(datetime, tz)| tz.from_utc_datetime(&datetime))
283 }
284
285 fn datetime_local_strategy() -> impl Strategy<Value = DateTime<Local>> {
286 (arb::<NaiveDateTime>()).prop_filter_map("valid local datetime", |naive| {
287 Local.from_local_datetime(&naive).single()
288 })
289 }
290
291 fn datetime_fixed_offset_strategy() -> impl Strategy<Value = DateTime<FixedOffset>> {
292 (arb::<NaiveDateTime>(), (-85_399..86_400)).prop_map(|(naive, offset)| {
293 FixedOffset::east_opt(offset)
294 .unwrap()
295 .from_local_datetime(&naive)
296 .single()
297 .unwrap()
298 })
299 }
300
301 proptest! {
302 #[test]
303 fn roundtrip_weekday(value in arb::<Weekday>()) {
304 roundtrip(value);
305 }
306
307 #[test]
308 fn roundtrip_month(value in arb::<Month>()) {
309 roundtrip(value);
310 }
311
312 #[test]
313 fn roundtrip_fixed_offset(value in arb::<FixedOffset>()) {
314 roundtrip(value);
315 }
316
317 #[test]
318 fn roundtrip_tz(value in arb::<Tz>()) {
319 roundtrip(value);
320 }
321
322 #[test]
323 fn roundtrip_datetime_utc(value in arb::<DateTime<Utc>>()) {
324 roundtrip(value);
325 }
326
327 #[test]
328 fn roundtrip_datetime_local(value in datetime_local_strategy()) {
329 roundtrip(value);
330 }
331
332 #[test]
333 fn roundtrip_datetime_fixed_offset(value in datetime_fixed_offset_strategy()) {
334 roundtrip(value);
335 }
336
337 #[test]
338 fn roundtrip_datetime_tz(value in datetime_tz_strategy()) {
339 roundtrip(value);
340 }
341
342 #[test]
343 fn roundtrip_naive_date(value in arb::<NaiveDate>()) {
344 roundtrip(value);
345 }
346
347 #[test]
348 fn roundtrip_naive_time(value in arb::<NaiveDate>()) {
349 roundtrip(value);
350 }
351
352 #[test]
353 fn roundtrip_naive_date_time(value in arb::<NaiveDateTime>()) {
354 roundtrip(value);
355 }
356 }
357}