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