1use chrono::{Datelike, Duration, TimeZone, Utc};
2use dcbor::prelude::*;
3
4use crate::{Error, Result};
5
6pub trait SerializableDate: Sized {
7 fn serialize_2_bytes(&self) -> Result<[u8; 2]>;
8 fn deserialize_2_bytes(bytes: &[u8; 2]) -> Result<Self>;
9
10 fn serialize_4_bytes(&self) -> Result<[u8; 4]>;
11 fn deserialize_4_bytes(bytes: &[u8; 4]) -> Result<Self>;
12
13 fn serialize_6_bytes(&self) -> Result<[u8; 6]>;
14 fn deserialize_6_bytes(bytes: &[u8; 6]) -> Result<Self>;
15}
16
17impl SerializableDate for Date {
18 fn serialize_2_bytes(&self) -> Result<[u8; 2]> {
19 let components = self.datetime();
20 let year = components.year();
21 let month = components.month();
22 let day = components.day();
23
24 let yy = year - 2023;
25 if !(0..128).contains(&yy) {
26 return Err(Error::YearOutOfRange { year });
27 }
28 if !(1..=12).contains(&month) || !(1..=31).contains(&day) {
29 return Err(Error::InvalidMonthOrDay { year, month, day });
30 }
31
32 let value = ((yy as u16) << 9) | ((month as u16) << 5) | (day as u16);
33 Ok(value.to_be_bytes())
34 }
35
36 fn deserialize_2_bytes(bytes: &[u8; 2]) -> Result<Self> {
37 let value = u16::from_be_bytes(*bytes);
38 let day = (value & 0b11111) as u32;
39 let month = ((value >> 5) & 0b1111) as u32;
40 let yy = ((value >> 9) & 0b1111111) as i32;
41 let year = yy + 2023;
42
43 if !(1..=12).contains(&month)
44 || !range_of_days_in_month(year, month).contains(&day)
45 {
46 return Err(Error::InvalidMonthOrDay { year, month, day });
47 }
48
49 let date = Utc
50 .with_ymd_and_hms(year, month, day, 0, 0, 0)
51 .single()
52 .ok_or_else(|| Error::InvalidDate {
53 details: format!(
54 "Cannot construct date {year}-{month:02}-{day:02}"
55 ),
56 })?;
57 Ok(Date::from_datetime(date))
58 }
59
60 fn serialize_4_bytes(&self) -> Result<[u8; 4]> {
61 let reference_date =
62 Utc.with_ymd_and_hms(2001, 1, 1, 0, 0, 0).single().unwrap();
63 let duration = self.datetime() - reference_date;
64 let seconds = duration.num_seconds();
65 let n = u32::try_from(seconds).map_err(|_| Error::DateOutOfRange {
66 details: "seconds value too large for u32".to_string(),
67 })?;
68 Ok(n.to_be_bytes())
69 }
70
71 fn deserialize_4_bytes(bytes: &[u8; 4]) -> Result<Self> {
72 let n = u32::from_be_bytes(*bytes);
73 let reference_date =
74 Utc.with_ymd_and_hms(2001, 1, 1, 0, 0, 0).single().unwrap();
75 let date = reference_date + chrono::Duration::seconds(n as i64);
76 Ok(Date::from_datetime(date))
77 }
78
79 fn serialize_6_bytes(&self) -> Result<[u8; 6]> {
80 let reference_date =
81 Utc.with_ymd_and_hms(2001, 1, 1, 0, 0, 0).single().unwrap();
82 let duration = self.datetime() - reference_date;
83 let milliseconds = duration.num_milliseconds();
84 let n =
85 u64::try_from(milliseconds).map_err(|_| Error::DateOutOfRange {
86 details: "milliseconds value too large for u64".to_string(),
87 })?;
88
89 if n > 0xe5940a78a7ff {
90 return Err(Error::DateOutOfRange {
91 details: "date exceeds maximum representable value".to_string(),
92 });
93 }
94
95 let bytes = n.to_be_bytes();
96 Ok(bytes[2..8].try_into().unwrap())
97 }
98
99 fn deserialize_6_bytes(bytes: &[u8; 6]) -> Result<Self> {
100 let mut full_bytes = [0u8; 8];
101 full_bytes[2..].copy_from_slice(bytes);
102 let n = u64::from_be_bytes(full_bytes);
103
104 if n > 0xe5940a78a7ff {
105 return Err(Error::DateOutOfRange {
106 details: "date exceeds maximum representable value".to_string(),
107 });
108 }
109
110 let reference_date =
111 Utc.with_ymd_and_hms(2001, 1, 1, 0, 0, 0).single().unwrap();
112 let date = reference_date + chrono::Duration::milliseconds(n as i64);
113 Ok(Date::from_datetime(date))
114 }
115}
116
117pub fn range_of_days_in_month(year: i32, month: u32) -> std::ops::Range<u32> {
118 let next_month = if month == 12 {
119 Utc.with_ymd_and_hms(year + 1, 1, 1, 0, 0, 0).unwrap()
120 } else {
121 Utc.with_ymd_and_hms(year, month + 1, 1, 0, 0, 0).unwrap()
122 };
123 let last_day = (next_month - Duration::days(1)).day();
124 1..last_day + 1
125}