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