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        #[inline(always)]
29        fn default_value(_: &mut ReadContext) -> Result<Self, Error> {
30            Ok($default)
31        }
32
33        #[inline(always)]
34        fn read_arc_any(
35            context: &mut ReadContext,
36        ) -> Result<Arc<dyn std::any::Any + Send + Sync>, Error> {
37            Ok(Arc::new(Self::read_data(context)?))
38        }
39
40        #[inline(always)]
41        fn reserved_space() -> usize {
42            $reserved
43        }
44
45        #[inline(always)]
46        fn static_type_id() -> TypeId {
47            $type_id
48        }
49
50        #[inline(always)]
51        fn write_type_info(context: &mut WriteContext) -> Result<(), Error> {
52            context.writer.write_u8($type_id as u8);
53            Ok(())
54        }
55
56        #[inline(always)]
57        fn read_type_info(context: &mut ReadContext) -> Result<(), Error> {
58            read_basic_type_info::<$ty>(context)
59        }
60    };
61}
62
63impl Serializer for Timestamp {
64    type Target = Self;
65
66    #[inline(always)]
67    fn write_data(value: &Self, context: &mut WriteContext) -> Result<(), Error> {
68        context.writer.write_i64(value.seconds());
69        context.writer.write_u32(value.subsec_nanos());
70        Ok(())
71    }
72
73    #[inline(always)]
74    fn read_data(context: &mut ReadContext) -> Result<Self, Error> {
75        Timestamp::new(context.reader.read_i64()?, context.reader.read_u32()?)
76    }
77
78    temporal_hooks!(
79        Timestamp,
80        TypeId::TIMESTAMP,
81        std::mem::size_of::<i64>() + std::mem::size_of::<u32>(),
82        Timestamp::default()
83    );
84}
85
86impl Serializer for Date {
87    type Target = Self;
88
89    #[inline(always)]
90    fn write_data(value: &Self, context: &mut WriteContext) -> Result<(), Error> {
91        let days = value.epoch_days();
92        if context.is_xlang() {
93            context.writer.write_var_i64(days);
94        } else {
95            let native_days = i32::try_from(days).map_err(|_| {
96                Error::invalid_data(format!("date day count {} exceeds native i32 range", days))
97            })?;
98            context.writer.write_i32(native_days);
99        }
100        Ok(())
101    }
102
103    #[inline(always)]
104    fn read_data(context: &mut ReadContext) -> Result<Self, Error> {
105        let days = if context.is_xlang() {
106            context.reader.read_var_i64()?
107        } else {
108            i64::from(context.reader.read_i32()?)
109        };
110        Ok(Date::from_epoch_days(days))
111    }
112
113    temporal_hooks!(Date, TypeId::DATE, 9, Date::default());
114}
115
116impl Serializer for Duration {
117    type Target = Self;
118
119    #[inline(always)]
120    fn write_data(value: &Self, context: &mut WriteContext) -> Result<(), Error> {
121        context.writer.write_var_i64(value.seconds());
122        context.writer.write_i32(value.subsec_nanos() as i32);
123        Ok(())
124    }
125
126    #[inline(always)]
127    fn read_data(context: &mut ReadContext) -> Result<Self, Error> {
128        Duration::new(context.reader.read_var_i64()?, context.reader.read_i32()?)
129    }
130
131    temporal_hooks!(
132        Duration,
133        TypeId::DURATION,
134        9 + std::mem::size_of::<i32>(),
135        Duration::default()
136    );
137}
138
139#[cfg(feature = "chrono")]
140mod chrono_support {
141    use super::*;
142    use chrono::{Duration as ChronoDuration, NaiveDate, NaiveDateTime};
143
144    impl Serializer for NaiveDateTime {
145        type Target = Self;
146
147        #[inline(always)]
148        fn write_data(value: &Self, context: &mut WriteContext) -> Result<(), Error> {
149            Timestamp::write_data(&Timestamp::from(*value), context)
150        }
151
152        #[inline(always)]
153        fn read_data(context: &mut ReadContext) -> Result<Self, Error> {
154            Timestamp::read_data(context)?.try_into()
155        }
156
157        temporal_hooks!(
158            NaiveDateTime,
159            TypeId::TIMESTAMP,
160            Timestamp::reserved_space(),
161            NaiveDateTime::default()
162        );
163    }
164
165    impl Serializer for NaiveDate {
166        type Target = Self;
167
168        #[inline(always)]
169        fn write_data(value: &Self, context: &mut WriteContext) -> Result<(), Error> {
170            Date::write_data(&Date::from(*value), context)
171        }
172
173        #[inline(always)]
174        fn read_data(context: &mut ReadContext) -> Result<Self, Error> {
175            Date::read_data(context)?.try_into()
176        }
177
178        temporal_hooks!(
179            NaiveDate,
180            TypeId::DATE,
181            Date::reserved_space(),
182            NaiveDate::default()
183        );
184    }
185
186    impl Serializer for ChronoDuration {
187        type Target = Self;
188
189        #[inline(always)]
190        fn write_data(value: &Self, context: &mut WriteContext) -> Result<(), Error> {
191            Duration::write_data(&Duration::try_from(*value)?, context)
192        }
193
194        #[inline(always)]
195        fn read_data(context: &mut ReadContext) -> Result<Self, Error> {
196            Duration::read_data(context)?.try_into()
197        }
198
199        temporal_hooks!(
200            ChronoDuration,
201            TypeId::DURATION,
202            Duration::reserved_space(),
203            ChronoDuration::zero()
204        );
205    }
206}
207
208#[cfg(test)]
209mod tests {
210    use super::*;
211    use crate::fory::Fory;
212
213    #[test]
214    fn test_temporal_carrier_serialization() {
215        let fory = Fory::builder().xlang(false).compatible(false).build();
216
217        let timestamps = [
218            Timestamp::UNIX_EPOCH,
219            Timestamp::new(1, 0).unwrap(),
220            Timestamp::new(-1, 999_999_999).unwrap(),
221        ];
222        for timestamp in timestamps {
223            let bytes = fory.serialize(&timestamp).unwrap();
224            let deserialized: Timestamp = fory.deserialize(&bytes).unwrap();
225            assert_eq!(timestamp, deserialized);
226        }
227
228        let dates = [
229            Date::UNIX_EPOCH,
230            Date::from_epoch_days(-1),
231            Date::from_epoch_days(18_628),
232        ];
233        for date in dates {
234            let bytes = fory.serialize(&date).unwrap();
235            let deserialized: Date = fory.deserialize(&bytes).unwrap();
236            assert_eq!(date, deserialized);
237        }
238
239        let durations = [
240            Duration::ZERO,
241            Duration::new(1, 0).unwrap(),
242            Duration::new(0, -1).unwrap(),
243            Duration::new(-123, 456_789).unwrap(),
244        ];
245        for duration in durations {
246            let bytes = fory.serialize(&duration).unwrap();
247            let deserialized: Duration = fory.deserialize(&bytes).unwrap();
248            assert_eq!(duration, deserialized);
249        }
250    }
251
252    #[test]
253    fn duration_negative_nanoseconds() {
254        assert_eq!(
255            Duration::new(0, -1).unwrap(),
256            Duration::from_normalized(-1, 999_999_999).unwrap()
257        );
258    }
259
260    #[cfg(feature = "chrono")]
261    #[test]
262    fn chrono_temporal_serialization() {
263        use chrono::{DateTime, Duration as ChronoDuration, NaiveDate, NaiveDateTime};
264
265        let fory = Fory::builder().xlang(false).compatible(false).build();
266        let date = NaiveDate::from_ymd_opt(2024, 2, 3).unwrap();
267        let timestamp = DateTime::from_timestamp(100, 1).unwrap().naive_utc();
268        let duration = ChronoDuration::nanoseconds(-1);
269
270        let bytes = fory.serialize(&date).unwrap();
271        let deserialized: NaiveDate = fory.deserialize(&bytes).unwrap();
272        assert_eq!(date, deserialized);
273
274        let bytes = fory.serialize(&timestamp).unwrap();
275        let deserialized: NaiveDateTime = fory.deserialize(&bytes).unwrap();
276        assert_eq!(timestamp, deserialized);
277
278        let bytes = fory.serialize(&duration).unwrap();
279        let deserialized: ChronoDuration = fory.deserialize(&bytes).unwrap();
280        assert_eq!(duration, deserialized);
281    }
282}