Skip to main content

deser_core/ext/bridges/
time.rs

1use ::time::{Month, OffsetDateTime, PrimitiveDateTime, UtcDateTime, UtcOffset};
2use alloc::string::ToString;
3
4use crate::error::Error;
5use crate::ext::known::{Bridge, impl_bridge, invalid, out_of_range};
6use crate::ext::{Date, Datetime, Duration, Offset, Time, Timestamp};
7
8#[cold]
9fn range_error(err: ::time::error::ComponentRange) -> Error {
10    out_of_range(err.to_string()).with_source(err)
11}
12
13fn date_to_known(value: ::time::Date) -> Result<Date, Error> {
14    match u16::try_from(value.year()) {
15        Ok(year) if year <= 9999 => Ok(Date {
16            year,
17            month: u8::from(value.month()),
18            day: value.day(),
19        }),
20        _ => Err(out_of_range("year out of range")),
21    }
22}
23
24fn time_to_known(value: ::time::Time) -> Time {
25    Time {
26        hour: value.hour(),
27        minute: value.minute(),
28        second: value.second(),
29        nanosecond: value.nanosecond(),
30    }
31}
32
33fn date_from_known(value: Date) -> Result<::time::Date, Error> {
34    let month = Month::try_from(value.month).map_err(range_error)?;
35    ::time::Date::from_calendar_date(i32::from(value.year), month, value.day).map_err(range_error)
36}
37
38fn time_from_known(value: Time) -> Result<::time::Time, Error> {
39    ::time::Time::from_hms_nano(
40        value.hour,
41        value.minute,
42        // leap seconds are not supported, they are clamped
43        value.second.min(59),
44        value.nanosecond,
45    )
46    .map_err(range_error)
47}
48
49impl Bridge for ::time::Date {
50    type Known = Datetime;
51
52    const EXPECTING: &'static str = "local date";
53
54    fn to_known(&self) -> Result<Datetime, Error> {
55        date_to_known(*self).map(Datetime::from)
56    }
57
58    fn from_known(value: Datetime) -> Result<::time::Date, Error> {
59        date_from_known(value.expect_local_date()?)
60    }
61}
62
63impl Bridge for ::time::Time {
64    type Known = Datetime;
65
66    const EXPECTING: &'static str = "local time";
67
68    fn to_known(&self) -> Result<Datetime, Error> {
69        Ok(Datetime::from(time_to_known(*self)))
70    }
71
72    fn from_known(value: Datetime) -> Result<::time::Time, Error> {
73        time_from_known(value.expect_local_time()?)
74    }
75}
76
77impl Bridge for PrimitiveDateTime {
78    type Known = Datetime;
79
80    const EXPECTING: &'static str = "local date-time";
81
82    fn to_known(&self) -> Result<Datetime, Error> {
83        Ok(Datetime {
84            date: Some(date_to_known(self.date())?),
85            time: Some(time_to_known(self.time())),
86            offset: None,
87        })
88    }
89
90    fn from_known(value: Datetime) -> Result<PrimitiveDateTime, Error> {
91        let (date, time) = value.expect_local_datetime()?;
92        Ok(PrimitiveDateTime::new(
93            date_from_known(date)?,
94            time_from_known(time)?,
95        ))
96    }
97}
98
99impl Bridge for OffsetDateTime {
100    type Known = Datetime;
101
102    const EXPECTING: &'static str = "offset date-time";
103
104    fn to_known(&self) -> Result<Datetime, Error> {
105        let offset = self.offset();
106        if offset.seconds_past_minute() != 0 {
107            return Err(out_of_range("offsets with seconds are not supported"));
108        }
109        Ok(Datetime {
110            date: Some(date_to_known(self.date())?),
111            time: Some(time_to_known(self.time())),
112            offset: Some(match offset.whole_minutes() {
113                0 => Offset::Z,
114                minutes => Offset::Custom { minutes },
115            }),
116        })
117    }
118
119    fn from_known(value: Datetime) -> Result<OffsetDateTime, Error> {
120        let (date, time, offset) = value.expect_offset_datetime()?;
121        let offset = UtcOffset::from_whole_seconds(i32::from(offset.minutes()) * 60)
122            .map_err(|_| invalid("invalid offset"))?;
123        Ok(OffsetDateTime::new_in_offset(
124            date_from_known(date)?,
125            time_from_known(time)?,
126            offset,
127        ))
128    }
129}
130
131impl Bridge for UtcDateTime {
132    type Known = Timestamp;
133
134    fn to_known(&self) -> Result<Timestamp, Error> {
135        let nanos = self.unix_timestamp_nanos();
136        Ok(Timestamp {
137            seconds: nanos.div_euclid(1_000_000_000) as i64,
138            nanosecond: nanos.rem_euclid(1_000_000_000) as u32,
139        })
140    }
141
142    fn from_known(value: Timestamp) -> Result<UtcDateTime, Error> {
143        let nanos = i128::from(value.seconds) * 1_000_000_000 + i128::from(value.nanosecond);
144        UtcDateTime::from_unix_timestamp_nanos(nanos).map_err(range_error)
145    }
146}
147
148impl Bridge for ::time::Duration {
149    type Known = Duration;
150
151    fn to_known(&self) -> Result<Duration, Error> {
152        Ok(Duration {
153            seconds: self.whole_seconds(),
154            nanosecond: self.subsec_nanoseconds(),
155        })
156    }
157
158    fn from_known(value: Duration) -> Result<::time::Duration, Error> {
159        if !value.is_valid() {
160            return Err(invalid("invalid duration"));
161        }
162        Ok(::time::Duration::new(value.seconds, value.nanosecond))
163    }
164}
165
166impl_bridge!(
167    ::time::Date,
168    ::time::Time,
169    PrimitiveDateTime,
170    OffsetDateTime,
171    UtcDateTime,
172    ::time::Duration,
173);