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::error::Error;
19use crate::resolver::context::ReadContext;
20use crate::resolver::context::WriteContext;
21use crate::resolver::type_resolver::TypeResolver;
22use crate::serializer::util::read_basic_type_info;
23use crate::serializer::ForyDefault;
24use crate::serializer::Serializer;
25use crate::types::TypeId;
26use crate::util::EPOCH;
27use chrono::{Duration as ChronoDuration, NaiveDate, NaiveDateTime};
28use std::mem;
29use std::time::Duration;
30
31impl Serializer for NaiveDateTime {
32    #[inline(always)]
33    fn fory_write_data(&self, context: &mut WriteContext) -> Result<(), Error> {
34        let dt = self.and_utc();
35        let seconds = dt.timestamp();
36        let nanos = dt.timestamp_subsec_nanos();
37        context.writer.write_i64(seconds);
38        context.writer.write_u32(nanos);
39        Ok(())
40    }
41
42    #[inline(always)]
43    fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
44        let seconds = context.reader.read_i64()?;
45        let nanos = context.reader.read_u32()?;
46        #[allow(deprecated)]
47        let result = NaiveDateTime::from_timestamp(seconds, nanos);
48        Ok(result)
49    }
50
51    #[inline(always)]
52    fn fory_reserved_space() -> usize {
53        mem::size_of::<i64>() + mem::size_of::<u32>()
54    }
55
56    #[inline(always)]
57    fn fory_get_type_id(_: &TypeResolver) -> Result<TypeId, Error> {
58        Ok(TypeId::TIMESTAMP)
59    }
60
61    #[inline(always)]
62    fn fory_type_id_dyn(&self, _: &TypeResolver) -> Result<TypeId, Error> {
63        Ok(TypeId::TIMESTAMP)
64    }
65
66    #[inline(always)]
67    fn fory_static_type_id() -> TypeId {
68        TypeId::TIMESTAMP
69    }
70
71    #[inline(always)]
72    fn as_any(&self) -> &dyn std::any::Any {
73        self
74    }
75
76    #[inline(always)]
77    fn fory_write_type_info(context: &mut WriteContext) -> Result<(), Error> {
78        context.writer.write_u8(TypeId::TIMESTAMP as u8);
79        Ok(())
80    }
81
82    #[inline(always)]
83    fn fory_read_type_info(context: &mut ReadContext) -> Result<(), Error> {
84        read_basic_type_info::<Self>(context)
85    }
86}
87
88impl Serializer for NaiveDate {
89    #[inline(always)]
90    fn fory_write_data(&self, context: &mut WriteContext) -> Result<(), Error> {
91        let days_since_epoch = self.signed_duration_since(EPOCH).num_days();
92        context.writer.write_i32(days_since_epoch as i32);
93        Ok(())
94    }
95
96    #[inline(always)]
97    fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
98        let days = context.reader.read_i32()?;
99        use chrono::TimeDelta;
100        let duration = TimeDelta::days(days as i64);
101        let result = EPOCH + duration;
102        Ok(result)
103    }
104
105    #[inline(always)]
106    fn fory_reserved_space() -> usize {
107        mem::size_of::<i32>()
108    }
109
110    #[inline(always)]
111    fn fory_get_type_id(_: &TypeResolver) -> Result<TypeId, Error> {
112        Ok(TypeId::DATE)
113    }
114
115    #[inline(always)]
116    fn fory_type_id_dyn(&self, _: &TypeResolver) -> Result<TypeId, Error> {
117        Ok(TypeId::DATE)
118    }
119
120    #[inline(always)]
121    fn fory_static_type_id() -> TypeId {
122        TypeId::DATE
123    }
124
125    #[inline(always)]
126    fn as_any(&self) -> &dyn std::any::Any {
127        self
128    }
129
130    #[inline(always)]
131    fn fory_write_type_info(context: &mut WriteContext) -> Result<(), Error> {
132        context.writer.write_u8(TypeId::DATE as u8);
133        Ok(())
134    }
135
136    #[inline(always)]
137    fn fory_read_type_info(context: &mut ReadContext) -> Result<(), Error> {
138        read_basic_type_info::<Self>(context)
139    }
140}
141
142impl ForyDefault for NaiveDateTime {
143    #[inline(always)]
144    fn fory_default() -> Self {
145        NaiveDateTime::default()
146    }
147}
148
149impl ForyDefault for NaiveDate {
150    #[inline(always)]
151    fn fory_default() -> Self {
152        NaiveDate::default()
153    }
154}
155
156impl Serializer for Duration {
157    #[inline(always)]
158    fn fory_write_data(&self, context: &mut WriteContext) -> Result<(), Error> {
159        let raw = self.as_secs();
160        if raw > i64::MAX as u64 {
161            return Err(Error::invalid_data(format!(
162                "std::time::Duration seconds {} exceeds i64::MAX and cannot be encoded as varint64",
163                raw
164            )));
165        }
166        let secs = raw as i64;
167        let nanos = self.subsec_nanos() as i32;
168        context.writer.write_varint64(secs);
169        context.writer.write_i32(nanos);
170        Ok(())
171    }
172
173    #[inline(always)]
174    fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
175        let secs = context.reader.read_varint64()?;
176        if secs < 0 {
177            return Err(Error::invalid_data(format!(
178                "negative duration seconds {} cannot be represented as std::time::Duration; use chrono::Duration instead",
179                secs
180            )));
181        }
182        let nanos = context.reader.read_i32()?;
183        if !(0..=999_999_999).contains(&nanos) {
184            // negative nanos will also be rejected, even though the xlang spec actually allows it.
185            // RFC 1040 (https://rust-lang.github.io/rfcs/1040-duration-reform.html#detailed-design) explicitly forbids negative nanoseconds.
186            // If supporting for negative nanoseconds is really needed, we can implement **normalization** similar to chrono and Java.
187            return Err(Error::invalid_data(format!(
188                "duration nanoseconds {} out of valid range [0, 999_999_999] for std::time::Duration",
189                nanos
190            )));
191        }
192        Ok(Duration::new(secs as u64, nanos as u32))
193    }
194
195    #[inline(always)]
196    fn fory_reserved_space() -> usize {
197        9 + mem::size_of::<i32>() // max varint64 is 9 bytes + 4 bytes for i32
198    }
199
200    #[inline(always)]
201    fn fory_get_type_id(_: &TypeResolver) -> Result<TypeId, Error> {
202        Ok(TypeId::DURATION)
203    }
204
205    #[inline(always)]
206    fn fory_type_id_dyn(&self, _: &TypeResolver) -> Result<TypeId, Error> {
207        Ok(TypeId::DURATION)
208    }
209
210    #[inline(always)]
211    fn fory_static_type_id() -> TypeId {
212        TypeId::DURATION
213    }
214
215    #[inline(always)]
216    fn as_any(&self) -> &dyn std::any::Any {
217        self
218    }
219
220    #[inline(always)]
221    fn fory_write_type_info(context: &mut WriteContext) -> Result<(), Error> {
222        context.writer.write_u8(TypeId::DURATION as u8);
223        Ok(())
224    }
225
226    #[inline(always)]
227    fn fory_read_type_info(context: &mut ReadContext) -> Result<(), Error> {
228        read_basic_type_info::<Self>(context)
229    }
230}
231
232impl ForyDefault for Duration {
233    #[inline(always)]
234    fn fory_default() -> Self {
235        Duration::ZERO
236    }
237}
238
239impl Serializer for ChronoDuration {
240    #[inline(always)]
241    fn fory_write_data(&self, context: &mut WriteContext) -> Result<(), Error> {
242        let secs = self.num_seconds();
243        let nanos = self.subsec_nanos();
244        context.writer.write_varint64(secs);
245        context.writer.write_i32(nanos);
246        Ok(())
247    }
248
249    #[inline(always)]
250    fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
251        let secs = context.reader.read_varint64()?;
252        let nanos = context.reader.read_i32()?;
253        if !(-999_999_999..=999_999_999).contains(&nanos) {
254            // chrono supports negative nanoseconds by applying normalization internally.
255            return Err(Error::invalid_data(format!(
256                "duration nanoseconds {} out of valid range [-999_999_999, 999_999_999]",
257                nanos
258            )));
259        }
260        ChronoDuration::try_seconds(secs) // the maximum seconds chrono supports is i64::MAX / 1_000, which is smaller than what the spec allows(i64::MAX)
261            .and_then(|d| d.checked_add(&ChronoDuration::nanoseconds(nanos as i64)))
262            .ok_or_else(|| {
263                Error::invalid_data(format!(
264                    "duration seconds {} out of chrono::Duration valid range",
265                    secs
266                ))
267            })
268    }
269
270    #[inline(always)]
271    fn fory_reserved_space() -> usize {
272        9 + mem::size_of::<i32>() // max varint64 is 9 bytes + 4 bytes for i32
273    }
274
275    #[inline(always)]
276    fn fory_get_type_id(_: &TypeResolver) -> Result<TypeId, Error> {
277        Ok(TypeId::DURATION)
278    }
279
280    #[inline(always)]
281    fn fory_type_id_dyn(&self, _: &TypeResolver) -> Result<TypeId, Error> {
282        Ok(TypeId::DURATION)
283    }
284
285    #[inline(always)]
286    fn fory_static_type_id() -> TypeId {
287        TypeId::DURATION
288    }
289
290    #[inline(always)]
291    fn as_any(&self) -> &dyn std::any::Any {
292        self
293    }
294
295    #[inline(always)]
296    fn fory_write_type_info(context: &mut WriteContext) -> Result<(), Error> {
297        context.writer.write_u8(TypeId::DURATION as u8);
298        Ok(())
299    }
300
301    #[inline(always)]
302    fn fory_read_type_info(context: &mut ReadContext) -> Result<(), Error> {
303        read_basic_type_info::<Self>(context)
304    }
305}
306
307impl ForyDefault for ChronoDuration {
308    #[inline(always)]
309    fn fory_default() -> Self {
310        ChronoDuration::zero()
311    }
312}
313
314#[cfg(test)]
315mod tests {
316    use super::*;
317    use crate::fory::Fory;
318
319    #[test]
320    fn test_std_duration_serialization() {
321        let fory = Fory::default();
322
323        // Test various durations
324        let test_cases = vec![
325            Duration::ZERO,
326            Duration::new(0, 0),
327            Duration::new(1, 0),
328            Duration::new(0, 1),
329            Duration::new(123, 456789),
330            Duration::new(i64::MAX as u64, 999_999_999),
331        ];
332
333        for duration in test_cases {
334            let bytes = fory.serialize(&duration).unwrap();
335            let deserialized: Duration = fory.deserialize(&bytes).unwrap();
336            assert_eq!(
337                duration, deserialized,
338                "Failed for duration: {:?}",
339                duration
340            );
341        }
342    }
343
344    #[test]
345    fn test_chrono_duration_serialization() {
346        let fory = Fory::default();
347
348        // Test various durations
349        let test_cases = vec![
350            ChronoDuration::zero(),
351            ChronoDuration::new(0, 0).unwrap(),
352            ChronoDuration::new(1, 0).unwrap(),
353            ChronoDuration::new(0, 1).unwrap(),
354            ChronoDuration::new(123, 456789).unwrap(),
355            ChronoDuration::seconds(-1),
356            ChronoDuration::nanoseconds(-1),
357            ChronoDuration::microseconds(-456789),
358            ChronoDuration::MAX,
359            ChronoDuration::MIN,
360        ];
361
362        for duration in test_cases {
363            let bytes = fory.serialize(&duration).unwrap();
364            let deserialized: ChronoDuration = fory.deserialize(&bytes).unwrap();
365            assert_eq!(
366                duration, deserialized,
367                "Failed for duration: {:?}",
368                duration
369            );
370        }
371    }
372
373    #[test]
374    fn test_chrono_duration_out_of_range_is_error() {
375        let fory = Fory::default();
376        let too_large = Duration::new(i64::MAX as u64, 0);
377        let bytes = fory.serialize(&too_large).unwrap();
378        let result: Result<ChronoDuration, _> = fory.deserialize(&bytes);
379        assert!(
380            result.is_err(),
381            "out-of-range seconds should not be deserialized into chrono::Duration!"
382        );
383    }
384
385    #[test]
386    fn test_negative_std_duration_read_is_error() {
387        let fory = Fory::default();
388        let negative_duration = ChronoDuration::seconds(-1);
389        let bytes = fory.serialize(&negative_duration).unwrap();
390        let result: Result<Duration, _> = fory.deserialize(&bytes);
391        assert!(
392            result.is_err(),
393            "negative duration should not be deserialized into std::time::Duration!"
394        );
395    }
396}