1use super::{Date, DateTimeParseError, ErroredDateTimeComponent, Time, TimeStamp, TimeStampOffset};
67use chrono::{
68 offset::LocalResult, DateTime, Datelike, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime,
69 TimeZone, Timelike,
70};
71
72impl TryFrom<TimeStamp> for NaiveDate {
75 type Error = DateTimeParseError;
76
77 fn try_from(value: TimeStamp) -> Result<Self, Self::Error> {
78 let TimeStamp {
79 year, month, day, ..
80 } = value;
81
82 let month = month.unwrap_or(1);
83 let day = day.unwrap_or(1);
84
85 let date = NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32).ok_or(
86 DateTimeParseError::InvalidComponentRange(ErroredDateTimeComponent::Date),
87 )?;
88 Ok(date)
89 }
90}
91
92impl TryFrom<Date> for NaiveDate {
95 type Error = DateTimeParseError;
96
97 fn try_from(value: Date) -> Result<Self, Self::Error> {
98 let Date { year, month, day } = value;
99
100 let month = month.unwrap_or(1);
101 let day = day.unwrap_or(1);
102
103 let date = NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32).ok_or(
104 DateTimeParseError::InvalidComponentRange(ErroredDateTimeComponent::Date),
105 )?;
106 Ok(date)
107 }
108}
109
110impl TryFrom<Time> for NaiveTime {
113 type Error = DateTimeParseError;
114
115 fn try_from(value: Time) -> Result<Self, Self::Error> {
116 let Time {
117 hour,
118 minute,
119 second,
120 microsecond,
121 ..
122 } = value;
123
124 let minute = minute.unwrap_or(0);
125 let second = second.unwrap_or(0);
126 let microsecond = microsecond.unwrap_or(0);
127
128 let time =
129 NaiveTime::from_hms_micro_opt(hour as u32, minute as u32, second as u32, microsecond)
130 .ok_or(DateTimeParseError::InvalidComponentRange(
131 ErroredDateTimeComponent::Time,
132 ))?;
133 Ok(time)
134 }
135}
136
137impl From<NaiveDate> for TimeStamp {
141 fn from(value: NaiveDate) -> Self {
142 let year = value.year() as u16;
143 let month = Some(value.month() as u8);
144 let day = Some(value.day() as u8);
145 let hour = None;
146 let minute = None;
147 let second = None;
148 let microsecond = None;
149 let offset = None;
150 TimeStamp {
151 year,
152 month,
153 day,
154 hour,
155 minute,
156 second,
157 microsecond,
158 offset,
159 }
160 }
161}
162
163impl From<NaiveDate> for Date {
166 fn from(value: NaiveDate) -> Self {
167 let year = value.year() as u16;
168 let month = Some(value.month() as u8);
169 let day = Some(value.day() as u8);
170 Date { year, month, day }
171 }
172}
173
174impl From<NaiveTime> for Time {
177 fn from(value: NaiveTime) -> Self {
178 let hour = value.hour() as u8;
179 let minute = Some(value.minute() as u8);
180 let second = Some(value.second() as u8);
181 let microsecond = Some(value.nanosecond() / 1000);
182 Time {
183 hour,
184 minute,
185 second,
186 microsecond,
187 offset: None,
188 }
189 }
190}
191
192impl TryFrom<TimeStamp> for NaiveDateTime {
195 type Error = DateTimeParseError;
196
197 fn try_from(value: TimeStamp) -> Result<Self, Self::Error> {
198 let date = NaiveDate::try_from(value)?;
199 let time = NaiveTime::from_hms_micro_opt(
200 value.hour.unwrap_or(0) as u32,
201 value.minute.unwrap_or(0) as u32,
202 value.second.unwrap_or(0) as u32,
203 value.microsecond.unwrap_or(0),
204 )
205 .ok_or(DateTimeParseError::InvalidComponentRange(
206 ErroredDateTimeComponent::Time,
207 ))?;
208 Ok(NaiveDateTime::new(date, time))
209 }
210}
211
212impl From<NaiveDateTime> for TimeStamp {
216 fn from(value: NaiveDateTime) -> Self {
217 let year = value.year() as u16;
218 let month = Some(value.month() as u8);
219 let day = Some(value.day() as u8);
220 let hour = Some(value.hour() as u8);
221 let minute = Some(value.minute() as u8);
222 let second = Some(value.second() as u8);
223 let microsecond = Some(value.nanosecond() / 1000);
224 let offset = None;
225 TimeStamp {
226 year,
227 month,
228 day,
229 hour,
230 minute,
231 second,
232 microsecond,
233 offset,
234 }
235 }
236}
237
238impl TryFrom<TimeStamp> for LocalResult<DateTime<FixedOffset>> {
244 type Error = DateTimeParseError;
245
246 fn try_from(value: TimeStamp) -> Result<Self, Self::Error> {
247 let TimeStamp {
248 year,
249 month,
250 day,
251 hour,
252 minute,
253 second,
254 microsecond,
255 offset,
256 } = value;
257
258 let month = month.unwrap_or(1);
259 let day = day.unwrap_or(1);
260 let date = NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32).ok_or(
261 DateTimeParseError::InvalidComponentRange(ErroredDateTimeComponent::Date),
262 )?;
263
264 let hour = hour.unwrap_or(0);
265 let minute = minute.unwrap_or(0);
266 let second = second.unwrap_or(0);
267 let microsecond = microsecond.unwrap_or(0);
268
269 let time =
270 NaiveTime::from_hms_micro_opt(hour as u32, minute as u32, second as u32, microsecond)
271 .ok_or(DateTimeParseError::InvalidComponentRange(
272 ErroredDateTimeComponent::Time,
273 ))?;
274
275 let offset = offset.unwrap_or_default();
276 let offset_hours = offset.hours as i32;
277 let offset_minutes = offset.minutes as i32;
278 let offset = FixedOffset::east_opt(offset_hours * 3600 + offset_minutes * 60).ok_or(
279 DateTimeParseError::InvalidComponentRange(ErroredDateTimeComponent::Offset),
280 )?;
281
282 let datetime = NaiveDateTime::new(date, time);
283 let datetime = datetime.and_local_timezone(offset);
284 Ok(datetime)
285 }
286}
287
288impl<Tz> TryFrom<TimeStamp> for DateTime<Tz>
297where
298 Tz: TimeZone,
299 DateTime<Tz>: From<DateTime<FixedOffset>>,
300{
301 type Error = DateTimeParseError;
302
303 fn try_from(value: TimeStamp) -> Result<Self, Self::Error> {
304 let datetime: LocalResult<DateTime<FixedOffset>> = LocalResult::try_from(value)?;
305 match datetime {
306 LocalResult::Single(datetime) => Ok(datetime.into()),
307 LocalResult::Ambiguous(earliest, latest) => Err(DateTimeParseError::AmbiguousTime(
308 earliest.to_rfc3339(),
309 latest.to_rfc3339(),
310 )),
311 LocalResult::None => Err(DateTimeParseError::InvalidComponentRange(
312 ErroredDateTimeComponent::DateTime,
313 )),
314 }
315 }
316}
317
318impl<Tz> From<DateTime<Tz>> for TimeStamp
322where
323 Tz: TimeZone,
324 DateTime<Tz>: Into<DateTime<FixedOffset>>,
325{
326 fn from(value: DateTime<Tz>) -> Self {
327 let datetime: DateTime<FixedOffset> = value.into();
328
329 let year = datetime.year() as u16;
330 let month = Some(datetime.month() as u8);
331 let day = Some(datetime.day() as u8);
332 let hour = Some(datetime.hour() as u8);
333 let minute = Some(datetime.minute() as u8);
334 let second = Some(datetime.second() as u8);
335 let microsecond = Some(datetime.nanosecond() / 1000);
336 let offset = Some(TimeStampOffset {
337 hours: (datetime.offset().local_minus_utc() / 3600) as i8,
338 minutes: (datetime.offset().local_minus_utc() % 3600) as u8,
339 });
340
341 TimeStamp {
342 year,
343 month,
344 day,
345 hour,
346 minute,
347 second,
348 microsecond,
349 offset,
350 }
351 }
352}
353
354#[cfg(test)]
355mod tests {
356 use crate::datetime::TimeStampOffset;
357 use chrono::{Timelike, Utc};
358
359 use super::*;
360
361 #[test]
362 fn can_convert_timestamp_to_date() {
363 let ts = TimeStamp {
364 year: 2023,
365 month: Some(3),
366 day: Some(12),
367 hour: Some(19),
368 minute: Some(59),
369 second: None,
370 microsecond: None,
371 offset: None,
372 };
373 let actual = NaiveDate::try_from(ts).unwrap();
374 assert_eq!(actual.year(), 2023);
375 assert_eq!(actual.month(), 3);
376 assert_eq!(actual.day(), 12);
377 }
378
379 #[test]
380 fn can_convert_timestamp_to_datetime_with_fixed_offset() {
381 let ts = TimeStamp {
382 year: 2023,
383 month: Some(3),
384 day: Some(12),
385 hour: Some(19),
386 minute: Some(59),
387 second: Some(5),
388 microsecond: Some(1234),
389 offset: Some(TimeStampOffset {
390 hours: -7,
391 minutes: 0,
392 }),
393 };
394 let actual = DateTime::<FixedOffset>::try_from(ts).unwrap();
395 assert_eq!(actual.year(), 2023);
396 assert_eq!(actual.month(), 3);
397 assert_eq!(actual.day(), 12);
398 assert_eq!(actual.hour(), 19);
399 assert_eq!(actual.minute(), 59);
400 assert_eq!(actual.second(), 5);
401 assert_eq!(actual.nanosecond(), 1234 * 1000);
402 assert_eq!(actual.offset().local_minus_utc() / 3600, -7);
403 assert_eq!(actual.offset().local_minus_utc() % 3600, 0);
404 }
405
406 #[test]
407 fn can_convert_timestamp_datetime_with_utc_offset() {
408 let ts = TimeStamp {
409 year: 2023,
410 month: Some(3),
411 day: Some(12),
412 hour: Some(19),
413 minute: Some(59),
414 second: Some(5),
415 microsecond: Some(1234),
416 offset: Some(TimeStampOffset {
417 hours: -7,
418 minutes: 0,
419 }),
420 };
421 let actual = DateTime::<Utc>::try_from(ts).unwrap();
422
423 assert_eq!(actual.year(), 2023);
424 assert_eq!(actual.month(), 3);
425 assert_eq!(actual.day(), 13);
426 assert_eq!(actual.hour(), 2);
427 assert_eq!(actual.minute(), 59);
428 assert_eq!(actual.second(), 5);
429 assert_eq!(actual.nanosecond(), 1234 * 1000);
430 }
431
432 #[test]
433 fn can_convert_datetime_to_timestamp() {
434 let datetime = Utc.from_utc_datetime(
435 &NaiveDate::from_ymd_opt(2023, 3, 12)
436 .unwrap()
437 .and_hms_opt(19, 59, 5)
438 .unwrap(),
439 );
440 let actual = TimeStamp::from(datetime);
441 assert_eq!(actual.year, 2023);
442 assert_eq!(actual.month, Some(3));
443 assert_eq!(actual.day, Some(12));
444 assert_eq!(actual.hour, Some(19));
445 assert_eq!(actual.minute, Some(59));
446 assert_eq!(actual.second, Some(5));
447 assert_eq!(actual.microsecond, Some(0));
448 assert_eq!(
449 actual.offset,
450 Some(TimeStampOffset {
451 hours: 0,
452 minutes: 0
453 })
454 );
455 }
456}