1use time::{Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset};
43
44use super::{DateTimeParseError, ErroredDateTimeComponent, TimeStamp, TimeStampOffset};
45
46impl TryFrom<TimeStamp> for Date {
47 type Error = DateTimeParseError;
48
49 fn try_from(value: TimeStamp) -> Result<Self, Self::Error> {
50 let TimeStamp {
51 year, month, day, ..
52 } = value;
53
54 match (year, month, day) {
55 (year, Some(month), Some(day)) => {
56 let month = Month::try_from(month).map_err(|_| {
57 DateTimeParseError::InvalidComponentRange(ErroredDateTimeComponent::Month)
58 })?;
59
60 Ok(
61 Date::from_calendar_date(year.into(), month, day).map_err(|_| {
62 DateTimeParseError::InvalidComponentRange(ErroredDateTimeComponent::Date)
63 })?,
64 )
65 }
66 (_year, Some(_), None) => Err(DateTimeParseError::MissingComponent(
67 ErroredDateTimeComponent::Day,
68 )),
69 (_year, None, _) => Err(DateTimeParseError::MissingComponent(
70 ErroredDateTimeComponent::Month,
71 )),
72 }
73 }
74}
75
76impl TryFrom<super::Date> for Date {
77 type Error = DateTimeParseError;
78
79 fn try_from(value: super::Date) -> Result<Self, Self::Error> {
80 let super::Date { year, month, day } = value;
81
82 match (year, month, day) {
83 (year, Some(month), Some(day)) => {
84 let month = Month::try_from(month).map_err(|_| {
85 DateTimeParseError::InvalidComponentRange(ErroredDateTimeComponent::Month)
86 })?;
87
88 Ok(
89 Date::from_calendar_date(year.into(), month, day).map_err(|_| {
90 DateTimeParseError::InvalidComponentRange(ErroredDateTimeComponent::Date)
91 })?,
92 )
93 }
94 (_year, Some(_), None) => Err(DateTimeParseError::MissingComponent(
95 ErroredDateTimeComponent::Day,
96 )),
97 (_year, None, _) => Err(DateTimeParseError::MissingComponent(
98 ErroredDateTimeComponent::Month,
99 )),
100 }
101 }
102}
103
104impl From<Date> for TimeStamp {
105 fn from(value: Date) -> Self {
106 let (year, month, day) = value.to_calendar_date();
107
108 TimeStamp {
109 year: year as u16,
110 month: Some(month as u8),
111 day: Some(day),
112 hour: None,
113 minute: None,
114 second: None,
115 microsecond: None,
116 offset: None,
117 }
118 }
119}
120
121impl From<Date> for super::Date {
122 fn from(value: Date) -> Self {
123 let (year, month, day) = value.to_calendar_date();
124
125 super::Date {
126 year: year as u16,
127 month: Some(month as u8),
128 day: Some(day),
129 }
130 }
131}
132
133impl TryFrom<TimeStamp> for PrimitiveDateTime {
134 type Error = DateTimeParseError;
135
136 fn try_from(value: TimeStamp) -> Result<Self, Self::Error> {
137 let date = Date::try_from(value)?;
138
139 if value.hour.is_none() {
140 return Err(DateTimeParseError::MissingComponent(
141 ErroredDateTimeComponent::Hour,
142 ));
143 }
144 if value.minute.is_none() {
145 return Err(DateTimeParseError::MissingComponent(
146 ErroredDateTimeComponent::Minute,
147 ));
148 }
149 if value.second.is_none() {
150 return Err(DateTimeParseError::MissingComponent(
151 ErroredDateTimeComponent::Second,
152 ));
153 }
154
155 let TimeStamp {
156 hour,
157 minute,
158 second,
159 microsecond,
160 ..
161 } = value;
162
163 let time = Time::from_hms_micro(
164 hour.unwrap(),
165 minute.unwrap(),
166 second.unwrap(),
167 microsecond.unwrap_or(0),
168 )
169 .map_err(|_| DateTimeParseError::InvalidComponentRange(ErroredDateTimeComponent::Time))?;
170
171 Ok(PrimitiveDateTime::new(date, time))
172 }
173}
174
175impl TryFrom<super::Time> for Time {
176 type Error = DateTimeParseError;
177
178 fn try_from(value: super::Time) -> Result<Self, Self::Error> {
179 if value.minute.is_none() {
180 return Err(DateTimeParseError::MissingComponent(
181 ErroredDateTimeComponent::Minute,
182 ));
183 }
184 if value.second.is_none() {
185 return Err(DateTimeParseError::MissingComponent(
186 ErroredDateTimeComponent::Second,
187 ));
188 }
189
190 let super::Time {
191 hour,
192 minute,
193 second,
194 microsecond,
195 ..
196 } = value;
197
198 Time::from_hms_micro(
199 hour,
200 minute.unwrap(),
201 second.unwrap(),
202 microsecond.unwrap_or(0),
203 )
204 .map_err(|_| DateTimeParseError::InvalidComponentRange(ErroredDateTimeComponent::Time))
205 }
206}
207
208impl From<PrimitiveDateTime> for TimeStamp {
209 fn from(value: PrimitiveDateTime) -> Self {
210 let date = value.date();
211 let time = value.time();
212
213 TimeStamp {
214 year: date.year() as u16,
215 month: Some(date.month().into()),
216 day: Some(date.day()),
217 hour: Some(time.hour()),
218 minute: Some(time.minute()),
219 second: Some(time.second()),
220 microsecond: Some(time.microsecond()),
221 offset: None,
222 }
223 }
224}
225
226impl From<Time> for super::Time {
227 fn from(value: Time) -> Self {
228 super::Time {
229 hour: value.hour(),
230 minute: Some(value.minute()),
231 second: Some(value.second()),
232 microsecond: Some(value.microsecond()),
233 offset: None,
234 }
235 }
236}
237
238impl TryFrom<TimeStamp> for OffsetDateTime {
239 type Error = DateTimeParseError;
240
241 fn try_from(value: TimeStamp) -> Result<Self, Self::Error> {
242 if value.offset.is_none() {
243 return Err(DateTimeParseError::MissingComponent(
244 ErroredDateTimeComponent::Offset,
245 ));
246 }
247
248 let datetime = PrimitiveDateTime::try_from(value)?;
249 let offset = value.offset.unwrap();
250 let offset = UtcOffset::from_hms(offset.hours, offset.minutes as i8, 0).map_err(|_| {
251 DateTimeParseError::InvalidComponentRange(ErroredDateTimeComponent::Offset)
252 })?;
253
254 let date = datetime.date();
255 let time = datetime.time();
256
257 Ok(OffsetDateTime::new_in_offset(date, time, offset))
258 }
259}
260
261impl From<OffsetDateTime> for TimeStamp {
262 fn from(value: OffsetDateTime) -> Self {
263 let date = value.date();
264 let time = value.time();
265 let offset = value.offset();
266
267 TimeStamp {
268 year: date.year() as u16,
269 month: Some(date.month().into()),
270 day: Some(date.day()),
271 hour: Some(time.hour()),
272 minute: Some(time.minute()),
273 second: Some(time.second()),
274 microsecond: Some(time.microsecond()),
275 offset: Some(TimeStampOffset {
276 hours: offset.whole_hours(),
277 minutes: (offset.whole_minutes() % 60).unsigned_abs() as u8,
278 }),
279 }
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286
287 #[test]
288 fn can_convert_timestamp_to_date() {
289 let ts = TimeStamp {
290 year: 2023,
291 month: Some(3),
292 day: Some(12),
293 hour: Some(19),
294 minute: Some(59),
295 second: None,
296 microsecond: None,
297 offset: None,
298 };
299 let actual = Date::try_from(ts).unwrap();
300 assert_eq!(
301 actual,
302 Date::from_calendar_date(2023, Month::March, 12).unwrap()
303 );
304 }
305
306 #[test]
307 fn can_convert_timestamp_to_offsetdateime() {
308 let ts = TimeStamp {
309 year: 2023,
310 month: Some(3),
311 day: Some(12),
312 hour: Some(19),
313 minute: Some(59),
314 second: Some(5),
315 microsecond: Some(1234),
316 offset: Some(TimeStampOffset {
317 hours: -7,
318 minutes: 0,
319 }),
320 };
321 let actual = OffsetDateTime::try_from(ts).unwrap();
322 assert_eq!(actual.year(), 2023);
323 assert_eq!(actual.month(), Month::March);
324 assert_eq!(actual.day(), 12);
325 assert_eq!(actual.hour(), 19);
326 assert_eq!(actual.minute(), 59);
327 assert_eq!(actual.second(), 5);
328 assert_eq!(actual.microsecond(), 1234);
329 assert_eq!(actual.offset(), UtcOffset::from_hms(-7, 0, 0).unwrap());
330 }
331}