Skip to main content

fory_core/serializer/
datetime.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use crate::context::{ReadContext, WriteContext};
19use crate::error::Error;
20use crate::serializer::util::read_basic_type_info;
21use crate::serializer::Serializer;
22use crate::type_id::TypeId;
23use crate::types::{Date, Duration, Timestamp};
24use std::sync::Arc;
25
26macro_rules! temporal_hooks {
27    ($ty:ty, $type_id:expr, $reserved:expr, $default:expr) => {
28        const READ_DATA_ALWAYS_ADVANCES: bool = true;
29
30        #[inline(always)]
31        fn default_value(_: &mut ReadContext) -> Result<Self, Error> {
32            Ok($default)
33        }
34
35        #[inline(always)]
36        fn read_arc_any(
37            context: &mut ReadContext,
38        ) -> Result<Arc<dyn std::any::Any + Send + Sync>, Error> {
39            Ok(Arc::new(Self::read_data(context)?))
40        }
41
42        #[inline(always)]
43        fn reserved_space() -> usize {
44            $reserved
45        }
46
47        #[inline(always)]
48        fn static_type_id() -> TypeId {
49            $type_id
50        }
51
52        #[inline(always)]
53        fn write_type_info(context: &mut WriteContext) -> Result<(), Error> {
54            context.writer.write_u8($type_id as u8);
55            Ok(())
56        }
57
58        #[inline(always)]
59        fn read_type_info(context: &mut ReadContext) -> Result<(), Error> {
60            read_basic_type_info::<$ty>(context)
61        }
62    };
63}
64
65impl Serializer for Timestamp {
66    type Target = Self;
67
68    #[inline(always)]
69    fn write_data(value: &Self, context: &mut WriteContext) -> Result<(), Error> {
70        context.writer.write_i64(value.seconds());
71        context.writer.write_u32(value.subsec_nanos());
72        Ok(())
73    }
74
75    #[inline(always)]
76    fn read_data(context: &mut ReadContext) -> Result<Self, Error> {
77        Timestamp::new(context.reader.read_i64()?, context.reader.read_u32()?)
78    }
79
80    temporal_hooks!(
81        Timestamp,
82        TypeId::TIMESTAMP,
83        std::mem::size_of::<i64>() + std::mem::size_of::<u32>(),
84        Timestamp::default()
85    );
86}
87
88impl Serializer for Date {
89    type Target = Self;
90
91    #[inline(always)]
92    fn write_data(value: &Self, context: &mut WriteContext) -> Result<(), Error> {
93        let days = value.epoch_days();
94        if context.is_xlang() {
95            context.writer.write_var_i64(days);
96        } else {
97            let native_days = i32::try_from(days).map_err(|_| {
98                Error::invalid_data(format!("date day count {} exceeds native i32 range", days))
99            })?;
100            context.writer.write_i32(native_days);
101        }
102        Ok(())
103    }
104
105    #[inline(always)]
106    fn read_data(context: &mut ReadContext) -> Result<Self, Error> {
107        let days = if context.is_xlang() {
108            context.reader.read_var_i64()?
109        } else {
110            i64::from(context.reader.read_i32()?)
111        };
112        Ok(Date::from_epoch_days(days))
113    }
114
115    temporal_hooks!(Date, TypeId::DATE, 9, Date::default());
116}
117
118impl Serializer for Duration {
119    type Target = Self;
120
121    #[inline(always)]
122    fn write_data(value: &Self, context: &mut WriteContext) -> Result<(), Error> {
123        context.writer.write_var_i64(value.seconds());
124        context.writer.write_i32(value.subsec_nanos() as i32);
125        Ok(())
126    }
127
128    #[inline(always)]
129    fn read_data(context: &mut ReadContext) -> Result<Self, Error> {
130        Duration::new(context.reader.read_var_i64()?, context.reader.read_i32()?)
131    }
132
133    temporal_hooks!(
134        Duration,
135        TypeId::DURATION,
136        9 + std::mem::size_of::<i32>(),
137        Duration::default()
138    );
139}
140
141#[cfg(feature = "chrono")]
142mod chrono_support {
143    use super::*;
144    use chrono::{Duration as ChronoDuration, NaiveDate, NaiveDateTime};
145
146    impl Serializer for NaiveDateTime {
147        type Target = Self;
148
149        #[inline(always)]
150        fn write_data(value: &Self, context: &mut WriteContext) -> Result<(), Error> {
151            Timestamp::write_data(&Timestamp::from(*value), context)
152        }
153
154        #[inline(always)]
155        fn read_data(context: &mut ReadContext) -> Result<Self, Error> {
156            Timestamp::read_data(context)?.try_into()
157        }
158
159        temporal_hooks!(
160            NaiveDateTime,
161            TypeId::TIMESTAMP,
162            Timestamp::reserved_space(),
163            NaiveDateTime::default()
164        );
165    }
166
167    impl Serializer for NaiveDate {
168        type Target = Self;
169
170        #[inline(always)]
171        fn write_data(value: &Self, context: &mut WriteContext) -> Result<(), Error> {
172            Date::write_data(&Date::from(*value), context)
173        }
174
175        #[inline(always)]
176        fn read_data(context: &mut ReadContext) -> Result<Self, Error> {
177            Date::read_data(context)?.try_into()
178        }
179
180        temporal_hooks!(
181            NaiveDate,
182            TypeId::DATE,
183            Date::reserved_space(),
184            NaiveDate::default()
185        );
186    }
187
188    impl Serializer for ChronoDuration {
189        type Target = Self;
190
191        #[inline(always)]
192        fn write_data(value: &Self, context: &mut WriteContext) -> Result<(), Error> {
193            Duration::write_data(&Duration::try_from(*value)?, context)
194        }
195
196        #[inline(always)]
197        fn read_data(context: &mut ReadContext) -> Result<Self, Error> {
198            Duration::read_data(context)?.try_into()
199        }
200
201        temporal_hooks!(
202            ChronoDuration,
203            TypeId::DURATION,
204            Duration::reserved_space(),
205            ChronoDuration::zero()
206        );
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213    use crate::fory::Fory;
214
215    #[test]
216    fn test_temporal_carrier_serialization() {
217        let fory = Fory::builder().xlang(false).compatible(false).build();
218
219        let timestamps = [
220            Timestamp::UNIX_EPOCH,
221            Timestamp::new(1, 0).unwrap(),
222            Timestamp::new(-1, 999_999_999).unwrap(),
223        ];
224        for timestamp in timestamps {
225            let bytes = fory.serialize(&timestamp).unwrap();
226            let deserialized: Timestamp = fory.deserialize(&bytes).unwrap();
227            assert_eq!(timestamp, deserialized);
228        }
229
230        let dates = [
231            Date::UNIX_EPOCH,
232            Date::from_epoch_days(-1),
233            Date::from_epoch_days(18_628),
234        ];
235        for date in dates {
236            let bytes = fory.serialize(&date).unwrap();
237            let deserialized: Date = fory.deserialize(&bytes).unwrap();
238            assert_eq!(date, deserialized);
239        }
240
241        let durations = [
242            Duration::ZERO,
243            Duration::new(1, 0).unwrap(),
244            Duration::new(0, -1).unwrap(),
245            Duration::new(-123, 456_789).unwrap(),
246        ];
247        for duration in durations {
248            let bytes = fory.serialize(&duration).unwrap();
249            let deserialized: Duration = fory.deserialize(&bytes).unwrap();
250            assert_eq!(duration, deserialized);
251        }
252    }
253
254    #[test]
255    fn duration_negative_nanoseconds() {
256        assert_eq!(
257            Duration::new(0, -1).unwrap(),
258            Duration::from_normalized(-1, 999_999_999).unwrap()
259        );
260    }
261
262    #[cfg(feature = "chrono")]
263    #[test]
264    fn chrono_temporal_serialization() {
265        use chrono::{DateTime, Duration as ChronoDuration, NaiveDate, NaiveDateTime};
266
267        let fory = Fory::builder().xlang(false).compatible(false).build();
268        let date = NaiveDate::from_ymd_opt(2024, 2, 3).unwrap();
269        let timestamp = DateTime::from_timestamp(100, 1).unwrap().naive_utc();
270        let duration = ChronoDuration::nanoseconds(-1);
271
272        let bytes = fory.serialize(&date).unwrap();
273        let deserialized: NaiveDate = fory.deserialize(&bytes).unwrap();
274        assert_eq!(date, deserialized);
275
276        let bytes = fory.serialize(&timestamp).unwrap();
277        let deserialized: NaiveDateTime = fory.deserialize(&bytes).unwrap();
278        assert_eq!(timestamp, deserialized);
279
280        let bytes = fory.serialize(&duration).unwrap();
281        let deserialized: ChronoDuration = fory.deserialize(&bytes).unwrap();
282        assert_eq!(duration, deserialized);
283    }
284}