Skip to main content

desert_core/features/
chrono.rs

1use 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}