1use core::convert::TryFrom;
8use core::num::NonZeroU8;
9
10use chrono::{Datelike, NaiveDate, Offset, TimeZone, Timelike};
11
12use crate::level_1::packed::{Certainty, PackedInt, PackedU8, PackedYear};
13use crate::{DateComplete, DateTime, GetTimezone, Time, TzOffset};
14
15impl GetTimezone for chrono::NaiveDate {
17 fn tz_offset(&self) -> TzOffset {
18 TzOffset::Unspecified
19 }
20}
21
22impl GetTimezone for chrono::DateTime<chrono::Utc> {
24 fn tz_offset(&self) -> TzOffset {
25 TzOffset::Utc
26 }
27}
28
29impl GetTimezone for chrono::DateTime<chrono::FixedOffset> {
31 fn tz_offset(&self) -> TzOffset {
32 let offset = self.offset();
33 TzOffset::Minutes(offset.local_minus_utc() / 60)
34 }
35}
36
37impl GetTimezone for chrono::DateTime<TzOffset> {
39 fn tz_offset(&self) -> TzOffset {
40 *self.offset()
41 }
42}
43
44impl<DT> From<DT> for DateTime
45where
46 DT: Datelike,
47 DT: Timelike,
48 DT: GetTimezone,
49{
50 fn from(chrono_dt: DT) -> DateTime {
51 let year = chrono_dt.year();
52 let month = NonZeroU8::new(chrono_dt.month() as u8).unwrap();
53 let day = NonZeroU8::new(chrono_dt.day() as u8).unwrap();
54 let hh = chrono_dt.hour() as u8;
55 let mm = chrono_dt.minute() as u8;
56 let ss = chrono_dt.second() as u8;
57 let date = DateComplete { year, month, day };
58 let date = date
59 .validate()
60 .expect("chrono::Datelike should return valid values");
61 let tz = chrono_dt.tz_offset();
62 let time = Time { hh, mm, ss, tz };
63 DateTime { date, time }
64 }
65}
66
67impl<DT> From<DT> for crate::level_0::Edtf
68where
69 DT: Datelike,
70 DT: Timelike,
71 DT: GetTimezone,
72{
73 fn from(chrono_dt: DT) -> crate::level_0::Edtf {
74 crate::level_0::Edtf::DateTime(chrono_dt.into())
75 }
76}
77
78impl<DT> From<DT> for crate::level_1::Edtf
79where
80 DT: Datelike,
81 DT: Timelike,
82 DT: GetTimezone,
83{
84 fn from(chrono_dt: DT) -> crate::level_1::Edtf {
85 crate::level_1::Edtf::DateTime(chrono_dt.into())
86 }
87}
88
89impl DateTime {
90 fn with_date(&self, date: DateComplete) -> Self {
91 let Self { date: _, time } = *self;
92 Self { date, time }
93 }
94 fn with_time(&self, time: Time) -> Self {
95 let Self { date, time: _ } = *self;
96 Self { date, time }
97 }
98}
99
100impl DateComplete {
101 pub fn to_chrono(&self) -> NaiveDate {
103 NaiveDate::from_ymd(self.year, self.month.get() as u32, self.day.get() as u32)
104 }
105}
106
107impl From<NaiveDate> for DateComplete {
109 fn from(naive: NaiveDate) -> Self {
110 Self {
111 year: naive.year(),
112 month: NonZeroU8::new(naive.month() as u8).unwrap(),
113 day: NonZeroU8::new(naive.day() as u8).unwrap(),
114 }
115 }
116}
117
118impl crate::level_0::Date {
119 pub fn to_chrono(&self) -> Option<NaiveDate> {
122 if let (Some(month), Some(day)) = (self.month, self.day) {
123 return Some(NaiveDate::from_ymd(
124 self.year,
125 month.get() as u32,
126 day.get() as u32,
127 ));
128 }
129 None
130 }
131}
132
133impl TryFrom<crate::level_0::Date> for NaiveDate {
135 type Error = ();
136 fn try_from(value: crate::level_0::Date) -> Result<Self, Self::Error> {
137 value.to_chrono().ok_or(())
138 }
139}
140
141impl crate::level_1::Date {
142 pub fn to_chrono(&self) -> Option<NaiveDate> {
145 if let (Some(month), Some(day)) = (self.month, self.day) {
146 return Some(NaiveDate::from_ymd(
147 self.year.unpack().0,
148 month.unpack().0 as u32,
149 day.unpack().0 as u32,
150 ));
151 }
152 None
153 }
154}
155
156impl TryFrom<crate::level_1::Date> for NaiveDate {
158 type Error = ();
159 fn try_from(value: crate::level_1::Date) -> Result<Self, Self::Error> {
160 value.to_chrono().ok_or(())
161 }
162}
163
164impl From<NaiveDate> for crate::level_1::Date {
167 fn from(naive: NaiveDate) -> Self {
168 Self {
169 year: PackedYear::pack(naive.year(), Default::default()).unwrap(),
170 month: PackedU8::pack(naive.month() as u8, Default::default()),
171 day: PackedU8::pack(naive.day() as u8, Default::default()),
172 certainty: Certainty::Certain,
173 }
174 }
175}
176
177impl Datelike for DateComplete {
180 fn year(&self) -> i32 {
181 self.year
182 }
183
184 fn month(&self) -> u32 {
185 self.month.get() as u32
186 }
187
188 fn month0(&self) -> u32 {
189 self.month.get() as u32 - 1
190 }
191
192 fn day(&self) -> u32 {
193 self.day.get() as u32
194 }
195
196 fn day0(&self) -> u32 {
197 self.day() - 1
198 }
199
200 fn ordinal(&self) -> u32 {
201 self.to_chrono().ordinal()
202 }
203 fn ordinal0(&self) -> u32 {
204 self.to_chrono().ordinal0()
205 }
206
207 fn weekday(&self) -> chrono::Weekday {
208 self.to_chrono().weekday()
209 }
210
211 fn iso_week(&self) -> chrono::IsoWeek {
212 self.to_chrono().iso_week()
213 }
214
215 fn with_year(&self, year: i32) -> Option<Self> {
216 self.to_chrono().with_year(year).map(Self::from)
217 }
218
219 fn with_month(&self, month: u32) -> Option<Self> {
220 self.to_chrono().with_month(month).map(Self::from)
221 }
222
223 fn with_month0(&self, month0: u32) -> Option<Self> {
224 self.to_chrono().with_month0(month0).map(Self::from)
225 }
226
227 fn with_day(&self, day: u32) -> Option<Self> {
228 self.to_chrono().with_day(day).map(Self::from)
229 }
230
231 fn with_day0(&self, day0: u32) -> Option<Self> {
232 self.to_chrono().with_day0(day0).map(Self::from)
233 }
234
235 fn with_ordinal(&self, ordinal: u32) -> Option<Self> {
236 self.to_chrono().with_ordinal(ordinal).map(Self::from)
237 }
238
239 fn with_ordinal0(&self, ordinal0: u32) -> Option<Self> {
240 self.to_chrono().with_ordinal0(ordinal0).map(Self::from)
241 }
242}
243
244impl Datelike for DateTime {
247 fn year(&self) -> i32 {
248 self.date.year()
249 }
250
251 fn month(&self) -> u32 {
252 self.date.month()
253 }
254
255 fn month0(&self) -> u32 {
256 self.date.month0()
257 }
258
259 fn day(&self) -> u32 {
260 self.date.day()
261 }
262
263 fn day0(&self) -> u32 {
264 self.date.day0()
265 }
266
267 fn ordinal(&self) -> u32 {
268 self.date.ordinal()
269 }
270
271 fn ordinal0(&self) -> u32 {
272 self.date.ordinal0()
273 }
274
275 fn weekday(&self) -> chrono::Weekday {
276 self.date.weekday()
277 }
278
279 fn iso_week(&self) -> chrono::IsoWeek {
280 self.date.iso_week()
281 }
282
283 fn with_year(&self, year: i32) -> Option<Self> {
284 self.date.with_year(year).map(|date| self.with_date(date))
285 }
286
287 fn with_month(&self, month: u32) -> Option<Self> {
288 self.date.with_month(month).map(|date| self.with_date(date))
289 }
290
291 fn with_month0(&self, month0: u32) -> Option<Self> {
292 self.date
293 .with_month0(month0)
294 .map(|date| self.with_date(date))
295 }
296
297 fn with_day(&self, day: u32) -> Option<Self> {
298 self.date.with_day(day).map(|date| self.with_date(date))
299 }
300
301 fn with_day0(&self, day0: u32) -> Option<Self> {
302 self.date.with_day0(day0).map(|date| self.with_date(date))
303 }
304
305 fn with_ordinal(&self, ordinal: u32) -> Option<Self> {
306 self.date
307 .with_ordinal(ordinal)
308 .map(|date| self.with_date(date))
309 }
310
311 fn with_ordinal0(&self, ordinal0: u32) -> Option<Self> {
312 self.date
313 .with_ordinal0(ordinal0)
314 .map(|date| self.with_date(date))
315 }
316}
317
318impl Timelike for Time {
319 fn hour(&self) -> u32 {
320 self.hh as u32
321 }
322
323 fn minute(&self) -> u32 {
324 self.mm as u32
325 }
326
327 fn second(&self) -> u32 {
328 self.ss as u32
329 }
330
331 fn nanosecond(&self) -> u32 {
332 0
333 }
334
335 fn with_hour(&self, hour: u32) -> Option<Self> {
336 if hour > 23 {
337 return None;
338 }
339 Some(Self {
340 hh: hour as u8,
341 ..*self
342 })
343 }
344
345 fn with_minute(&self, min: u32) -> Option<Self> {
346 if min > 59 {
347 return None;
348 }
349 Some(Self {
350 mm: min as u8,
351 ..*self
352 })
353 }
354
355 fn with_second(&self, sec: u32) -> Option<Self> {
356 if sec > 60 {
357 return None;
358 }
359 if sec == 60 && !(self.hh == 23 && self.mm == 59) {
360 return None;
361 }
362 Some(Self {
363 ss: sec as u8,
364 ..*self
365 })
366 }
367
368 fn with_nanosecond(&self, _nano: u32) -> Option<Self> {
369 Some(*self)
370 }
371}
372
373impl Timelike for DateTime {
374 fn hour(&self) -> u32 {
375 self.time.hour()
376 }
377
378 fn minute(&self) -> u32 {
379 self.time.minute()
380 }
381
382 fn second(&self) -> u32 {
383 self.time.second()
384 }
385
386 fn nanosecond(&self) -> u32 {
387 self.time.nanosecond()
388 }
389
390 fn with_hour(&self, hour: u32) -> Option<Self> {
391 self.time.with_hour(hour).map(|t| self.with_time(t))
392 }
393
394 fn with_minute(&self, min: u32) -> Option<Self> {
395 self.time.with_minute(min).map(|t| self.with_time(t))
396 }
397
398 fn with_second(&self, sec: u32) -> Option<Self> {
399 self.time.with_second(sec).map(|t| self.with_time(t))
400 }
401
402 fn with_nanosecond(&self, _nano: u32) -> Option<Self> {
403 Some(*self)
404 }
405}
406
407impl Offset for TzOffset {
408 fn fix(&self) -> chrono::FixedOffset {
409 match *self {
410 TzOffset::Unspecified => chrono::FixedOffset::east(0),
411 TzOffset::Utc => chrono::FixedOffset::east(0),
412 TzOffset::Hours(h) => chrono::FixedOffset::east(h * 3600),
413 TzOffset::Minutes(min) => chrono::FixedOffset::east(min * 60),
414 }
415 }
416}
417
418impl TimeZone for TzOffset {
419 type Offset = Self;
420
421 fn from_offset(offset: &Self::Offset) -> Self {
422 *offset
423 }
424
425 fn offset_from_local_date(&self, _local: &NaiveDate) -> chrono::LocalResult<Self::Offset> {
426 chrono::LocalResult::Single(*self)
427 }
428
429 fn offset_from_local_datetime(
430 &self,
431 _local: &chrono::NaiveDateTime,
432 ) -> chrono::LocalResult<Self::Offset> {
433 chrono::LocalResult::Single(*self)
434 }
435
436 fn offset_from_utc_date(&self, _utc: &NaiveDate) -> Self::Offset {
437 *self
438 }
439
440 fn offset_from_utc_datetime(&self, _utc: &chrono::NaiveDateTime) -> Self::Offset {
441 *self
442 }
443}
444
445#[test]
446fn timezone_impl() {
447 let off = TzOffset::Hours(4);
448 let ch = off.ymd(2019, 8, 7).and_hms(19, 7, 56);
449 let edtf = crate::level_1::Edtf::from(ch).as_datetime();
450 assert_eq!(
451 edtf,
452 Some(DateTime {
453 date: DateComplete::from_ymd(2019, 8, 7),
454 time: Time {
455 hh: 19,
456 mm: 7,
457 ss: 56,
458 tz: TzOffset::Hours(4)
459 }
460 })
461 );
462}
463
464#[test]
465fn timezone_impl_unspec() {
466 let off = TzOffset::Unspecified;
467 let ch = off.ymd(2019, 8, 7).and_hms(19, 7, 56);
468 let edtf = crate::level_1::Edtf::from(ch).as_datetime();
469 assert_eq!(
470 edtf,
471 Some(DateTime {
472 date: DateComplete::from_ymd(2019, 8, 7),
473 time: Time {
474 hh: 19,
475 mm: 7,
476 ss: 56,
477 tz: TzOffset::Unspecified,
478 }
479 })
480 );
481}
482
483#[cfg(test)]
484mod test {
485 #[test]
486 fn to_chrono() {
487 use crate::level_1::Edtf;
488 use chrono::TimeZone;
489 let utc = chrono::Utc;
490 assert_eq!(
491 Edtf::parse("2004-02-29T01:47:00+00:00")
492 .unwrap()
493 .as_datetime()
494 .unwrap()
495 .to_chrono(&utc),
496 utc.ymd(2004, 02, 29).and_hms(01, 47, 00)
497 );
498 assert_eq!(
499 Edtf::parse("2004-02-29T01:47:00")
500 .unwrap()
501 .as_datetime()
502 .unwrap()
503 .to_chrono(&utc),
504 utc.ymd(2004, 02, 29).and_hms(01, 47, 00)
505 );
506 }
507}