deser_core/ext/bridges/
jiff.rs1use ::jiff::civil;
2use ::jiff::tz::{self, TimeZone};
3use ::jiff::{SignedDuration, Zoned};
4use alloc::string::ToString;
5
6use crate::error::Error;
7use crate::ext::known::{Bridge, impl_bridge, invalid, out_of_range};
8use crate::ext::{Date, Datetime, Duration, Offset, Time, Timestamp};
9
10#[cold]
11fn jiff_error(err: ::jiff::Error) -> Error {
12 #[allow(unused_mut)]
13 let mut rv = invalid(err.to_string());
14 #[cfg(feature = "std")]
16 rv.set_source(err);
17 rv
18}
19
20fn date_to_known(value: civil::Date) -> Result<Date, Error> {
21 match u16::try_from(value.year()) {
22 Ok(year) => Ok(Date {
23 year,
24 month: value.month() as u8,
25 day: value.day() as u8,
26 }),
27 Err(_) => Err(out_of_range("negative years are not supported")),
28 }
29}
30
31fn time_to_known(value: civil::Time) -> Time {
32 Time {
33 hour: value.hour() as u8,
34 minute: value.minute() as u8,
35 second: value.second() as u8,
36 nanosecond: value.subsec_nanosecond() as u32,
37 }
38}
39
40fn date_from_known(value: Date) -> Result<civil::Date, Error> {
41 let year = i16::try_from(value.year).map_err(|_| out_of_range("year out of range"))?;
42 civil::Date::new(year, value.month as i8, value.day as i8).map_err(jiff_error)
43}
44
45fn time_from_known(value: Time) -> Result<civil::Time, Error> {
46 civil::Time::new(
47 value.hour as i8,
48 value.minute as i8,
49 value.second.min(59) as i8,
51 value.nanosecond as i32,
52 )
53 .map_err(jiff_error)
54}
55
56impl Bridge for civil::Date {
57 type Known = Datetime;
58
59 const EXPECTING: &'static str = "local date";
60
61 fn to_known(&self) -> Result<Datetime, Error> {
62 date_to_known(*self).map(Datetime::from)
63 }
64
65 fn from_known(value: Datetime) -> Result<civil::Date, Error> {
66 date_from_known(value.expect_local_date()?)
67 }
68
69 fn parse_fallback(value: &str) -> Option<civil::Date> {
70 value.parse().ok()
71 }
72}
73
74impl Bridge for civil::Time {
75 type Known = Datetime;
76
77 const EXPECTING: &'static str = "local time";
78
79 fn to_known(&self) -> Result<Datetime, Error> {
80 Ok(Datetime::from(time_to_known(*self)))
81 }
82
83 fn from_known(value: Datetime) -> Result<civil::Time, Error> {
84 time_from_known(value.expect_local_time()?)
85 }
86
87 fn parse_fallback(value: &str) -> Option<civil::Time> {
88 value.parse().ok()
89 }
90}
91
92impl Bridge for civil::DateTime {
93 type Known = Datetime;
94
95 const EXPECTING: &'static str = "local date-time";
96
97 fn to_known(&self) -> Result<Datetime, Error> {
98 Ok(Datetime {
99 date: Some(date_to_known(self.date())?),
100 time: Some(time_to_known(self.time())),
101 offset: None,
102 })
103 }
104
105 fn from_known(value: Datetime) -> Result<civil::DateTime, Error> {
106 let (date, time) = value.expect_local_datetime()?;
107 Ok(civil::DateTime::from_parts(
108 date_from_known(date)?,
109 time_from_known(time)?,
110 ))
111 }
112
113 fn parse_fallback(value: &str) -> Option<civil::DateTime> {
114 value.parse().ok()
115 }
116}
117
118impl Bridge for Zoned {
119 type Known = Datetime;
120
121 const EXPECTING: &'static str = "offset date-time";
122
123 fn to_known(&self) -> Result<Datetime, Error> {
125 let seconds = self.offset().seconds();
126 if seconds % 60 != 0 {
127 return Err(out_of_range("offsets with seconds are not supported"));
128 }
129 Ok(Datetime {
130 date: Some(date_to_known(self.date())?),
131 time: Some(time_to_known(self.time())),
132 offset: Some(match seconds / 60 {
133 0 => Offset::Z,
134 minutes => Offset::Custom {
135 minutes: minutes as i16,
136 },
137 }),
138 })
139 }
140
141 fn from_known(value: Datetime) -> Result<Zoned, Error> {
144 let (date, time, offset) = value.expect_offset_datetime()?;
145 let datetime = civil::DateTime::from_parts(date_from_known(date)?, time_from_known(time)?);
146 let offset =
147 tz::Offset::from_seconds(i32::from(offset.minutes()) * 60).map_err(jiff_error)?;
148 TimeZone::fixed(offset)
149 .to_zoned(datetime)
150 .map_err(jiff_error)
151 }
152
153 fn parse_fallback(value: &str) -> Option<Zoned> {
154 value.parse().ok()
155 }
156}
157
158impl Bridge for ::jiff::Timestamp {
159 type Known = Timestamp;
160
161 fn to_known(&self) -> Result<Timestamp, Error> {
162 let nanos = self.as_nanosecond();
163 Ok(Timestamp {
164 seconds: nanos.div_euclid(1_000_000_000) as i64,
165 nanosecond: nanos.rem_euclid(1_000_000_000) as u32,
166 })
167 }
168
169 fn from_known(value: Timestamp) -> Result<::jiff::Timestamp, Error> {
170 let nanos = i128::from(value.seconds) * 1_000_000_000 + i128::from(value.nanosecond);
171 ::jiff::Timestamp::from_nanosecond(nanos).map_err(jiff_error)
172 }
173
174 fn parse_fallback(value: &str) -> Option<::jiff::Timestamp> {
175 value.parse().ok()
176 }
177}
178
179impl Bridge for SignedDuration {
180 type Known = Duration;
181
182 fn to_known(&self) -> Result<Duration, Error> {
183 Ok(Duration {
184 seconds: self.as_secs(),
185 nanosecond: self.subsec_nanos(),
186 })
187 }
188
189 fn from_known(value: Duration) -> Result<SignedDuration, Error> {
190 if !value.is_valid() {
191 return Err(invalid("invalid duration"));
192 }
193 Ok(SignedDuration::new(value.seconds, value.nanosecond))
194 }
195
196 fn parse_fallback(value: &str) -> Option<SignedDuration> {
197 value.parse().ok()
198 }
199}
200
201impl_bridge!(
202 civil::Date,
203 civil::Time,
204 civil::DateTime,
205 Zoned,
206 ::jiff::Timestamp,
207 SignedDuration,
208);