picky_asn1/
date.rs

1use serde::{de, ser, Deserializer, Serializer};
2use std::fmt;
3
4pub trait TimeRepr
5where
6    Self: Sized,
7{
8    fn serialize<S>(
9        date: &Date<Self>,
10        serializer: S,
11    ) -> Result<<S as ser::Serializer>::Ok, <S as ser::Serializer>::Error>
12    where
13        S: ser::Serializer;
14
15    fn deserialize<'de, D>(deserializer: D) -> Result<Date<Self>, <D as de::Deserializer<'de>>::Error>
16    where
17        D: de::Deserializer<'de>;
18}
19
20/// A basic Date struct.
21#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
22pub struct Date<TR: TimeRepr> {
23    year: u16,
24    month: u8,
25    day: u8,
26    hour: u8,
27    minute: u8,
28    second: u8,
29    _pd: std::marker::PhantomData<TR>,
30}
31
32impl<TR: TimeRepr> Date<TR> {
33    /// Create a new Date without validation.
34    ///
35    /// # Safety
36    ///
37    /// You have to make sure you're not building an invalid date.
38    pub unsafe fn new_unchecked(year: u16, month: u8, day: u8, hour: u8, minute: u8, second: u8) -> Date<TR> {
39        Self {
40            year,
41            month,
42            day,
43            hour,
44            minute,
45            second,
46            _pd: std::marker::PhantomData,
47        }
48    }
49
50    pub fn new(year: u16, month: u8, day: u8, hour: u8, minute: u8, second: u8) -> Option<Date<TR>> {
51        if (1..=12).contains(&month) && (1..=32).contains(&day) && hour < 24 && minute < 60 && second < 60 {
52            Some(Self {
53                year,
54                month,
55                day,
56                hour,
57                minute,
58                second,
59                _pd: std::marker::PhantomData,
60            })
61        } else {
62            None
63        }
64    }
65
66    pub fn year(&self) -> u16 {
67        self.year
68    }
69
70    pub fn month(&self) -> u8 {
71        self.month
72    }
73
74    pub fn day(&self) -> u8 {
75        self.day
76    }
77
78    pub fn hour(&self) -> u8 {
79        self.hour
80    }
81
82    pub fn minute(&self) -> u8 {
83        self.minute
84    }
85
86    pub fn second(&self) -> u8 {
87        self.second
88    }
89}
90
91impl<TR: TimeRepr> ser::Serialize for Date<TR> {
92    fn serialize<S>(&self, serializer: S) -> Result<<S as ser::Serializer>::Ok, <S as ser::Serializer>::Error>
93    where
94        S: ser::Serializer,
95    {
96        TR::serialize(self, serializer)
97    }
98}
99
100impl<'de, TR: TimeRepr> de::Deserialize<'de> for Date<TR> {
101    fn deserialize<D>(deserializer: D) -> Result<Self, <D as de::Deserializer<'de>>::Error>
102    where
103        D: de::Deserializer<'de>,
104    {
105        TR::deserialize(deserializer)
106    }
107}
108
109trait DateDigitReader {
110    fn read_digit(&self, idx: usize) -> u8;
111
112    #[inline]
113    fn read_and_merge_with_next(&self, idx: usize) -> u8 {
114        self.read_digit(idx) * 10 + self.read_digit(idx + 1)
115    }
116}
117
118impl DateDigitReader for [u8] {
119    #[inline]
120    fn read_digit(&self, idx: usize) -> u8 {
121        self[idx] & 0x0F
122    }
123}
124
125#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
126pub struct UTCTimeRepr;
127pub type UTCTime = Date<UTCTimeRepr>;
128
129impl TimeRepr for UTCTimeRepr {
130    fn serialize<S>(date: &Date<UTCTimeRepr>, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
131    where
132        S: ser::Serializer,
133    {
134        let mut encoded = [
135            0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A,
136        ];
137
138        let year = if date.year() >= 2000 {
139            date.year() - 2000
140        } else {
141            date.year() - 1900
142        };
143
144        encoded[0] |= (year / 10) as u8;
145        encoded[1] |= (year % 10) as u8;
146        encoded[2] |= date.month() / 10;
147        encoded[3] |= date.month() % 10;
148        encoded[4] |= date.day() / 10;
149        encoded[5] |= date.day() % 10;
150        encoded[6] |= date.hour() / 10;
151        encoded[7] |= date.hour() % 10;
152        encoded[8] |= date.minute() / 10;
153        encoded[9] |= date.minute() % 10;
154        encoded[10] |= date.second() / 10;
155        encoded[11] |= date.second() % 10;
156
157        serializer.serialize_bytes(&encoded)
158    }
159
160    fn deserialize<'de, D>(deserializer: D) -> Result<Date<UTCTimeRepr>, <D as Deserializer<'de>>::Error>
161    where
162        D: de::Deserializer<'de>,
163    {
164        struct Visitor;
165
166        impl<'de> de::Visitor<'de> for Visitor {
167            type Value = Date<UTCTimeRepr>;
168
169            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
170                formatter.write_str("a valid buffer representing an Asn1 UTCTime")
171            }
172
173            fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
174            where
175                E: de::Error,
176            {
177                if v.len() != 13 {
178                    return Err(E::invalid_value(
179                        de::Unexpected::Other("unsupported date format"),
180                        &"a valid buffer representing an Asn1 UTCTime (exactly 13 bytes required)",
181                    ));
182                }
183
184                let yyyy = {
185                    let yy = v.read_and_merge_with_next(0) as u16;
186                    if yy >= 50 {
187                        1900 + yy
188                    } else {
189                        2000 + yy
190                    }
191                };
192                let month = v.read_and_merge_with_next(2);
193                let day = v.read_and_merge_with_next(4);
194                let hour = v.read_and_merge_with_next(6);
195                let minute = v.read_and_merge_with_next(8);
196                let second = v.read_and_merge_with_next(10);
197                let dt = Date::new(yyyy, month, day, hour, minute, second).ok_or_else(|| {
198                    E::invalid_value(
199                        de::Unexpected::Other("invalid parameters provided to Date constructor"),
200                        &"valid parameters for Date",
201                    )
202                })?;
203
204                Ok(dt)
205            }
206        }
207
208        deserializer.deserialize_bytes(Visitor)
209    }
210}
211
212#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
213pub struct GeneralizedTimeRepr;
214pub type GeneralizedTime = Date<GeneralizedTimeRepr>;
215
216impl TimeRepr for GeneralizedTimeRepr {
217    fn serialize<S>(
218        date: &Date<GeneralizedTimeRepr>,
219        serializer: S,
220    ) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
221    where
222        S: ser::Serializer,
223    {
224        let mut encoded = [
225            0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A,
226        ];
227
228        encoded[0] |= (date.year() / 1000) as u8;
229        encoded[1] |= ((date.year() % 1000) / 100) as u8;
230        encoded[2] |= ((date.year() % 100) / 10) as u8;
231        encoded[3] |= (date.year() % 10) as u8;
232        encoded[4] |= date.month() / 10;
233        encoded[5] |= date.month() % 10;
234        encoded[6] |= date.day() / 10;
235        encoded[7] |= date.day() % 10;
236        encoded[8] |= date.hour() / 10;
237        encoded[9] |= date.hour() % 10;
238        encoded[10] |= date.minute() / 10;
239        encoded[11] |= date.minute() % 10;
240        encoded[12] |= date.second() / 10;
241        encoded[13] |= date.second() % 10;
242
243        serializer.serialize_bytes(&encoded)
244    }
245
246    fn deserialize<'de, D>(deserializer: D) -> Result<Date<GeneralizedTimeRepr>, <D as Deserializer<'de>>::Error>
247    where
248        D: de::Deserializer<'de>,
249    {
250        struct Visitor;
251
252        impl<'de> de::Visitor<'de> for Visitor {
253            type Value = Date<GeneralizedTimeRepr>;
254
255            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
256                formatter.write_str("a valid buffer representing an Asn1 GeneralizedTime")
257            }
258
259            fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
260            where
261                E: de::Error,
262            {
263                if v.len() != 15 {
264                    return Err(E::invalid_value(
265                        de::Unexpected::Other("unsupported date format"),
266                        &"a valid buffer representing an Asn1 GeneralizedTime (exactly 15 bytes required)",
267                    ));
268                }
269
270                let yyyy = v.read_and_merge_with_next(0) as u16 * 100 + v.read_and_merge_with_next(2) as u16;
271                let month = v.read_and_merge_with_next(4);
272                let day = v.read_and_merge_with_next(6);
273                let hour = v.read_and_merge_with_next(8);
274                let minute = v.read_and_merge_with_next(10);
275                let second = v.read_and_merge_with_next(12);
276                let dt = Date::new(yyyy, month, day, hour, minute, second).ok_or_else(|| {
277                    E::invalid_value(
278                        de::Unexpected::Other("invalid parameters provided to Date constructor"),
279                        &"valid parameters for Date",
280                    )
281                })?;
282
283                Ok(dt)
284            }
285        }
286
287        deserializer.deserialize_bytes(Visitor)
288    }
289}
290
291#[cfg(feature = "time_conversion")]
292mod time_convert {
293    use super::*;
294    use time::{OffsetDateTime, PrimitiveDateTime};
295
296    impl<TR: TimeRepr> From<PrimitiveDateTime> for Date<TR> {
297        fn from(d: PrimitiveDateTime) -> Self {
298            Self::from(d.assume_utc())
299        }
300    }
301
302    impl<TR: TimeRepr> TryFrom<Date<TR>> for PrimitiveDateTime {
303        type Error = time::error::ComponentRange;
304
305        fn try_from(d: Date<TR>) -> Result<Self, Self::Error> {
306            let date = time::Date::from_calendar_date(i32::from(d.year), time::Month::try_from(d.month)?, d.day)?;
307            let time = time::Time::from_hms(d.hour, d.minute, d.second)?;
308            Ok(Self::new(date, time))
309        }
310    }
311
312    impl<TR: TimeRepr> From<OffsetDateTime> for Date<TR> {
313        fn from(d: OffsetDateTime) -> Self {
314            Self {
315                year: u16::try_from(d.year()).unwrap(),
316                month: u8::from(d.month()),
317                day: d.day(),
318                hour: d.hour(),
319                minute: d.minute(),
320                second: d.second(),
321                _pd: std::marker::PhantomData,
322            }
323        }
324    }
325
326    impl<TR: TimeRepr> TryFrom<Date<TR>> for OffsetDateTime {
327        type Error = time::error::ComponentRange;
328
329        fn try_from(d: Date<TR>) -> Result<Self, Self::Error> {
330            Ok(PrimitiveDateTime::try_from(d)?.assume_utc())
331        }
332    }
333}
334
335#[cfg(feature = "chrono_conversion")]
336mod chrono_convert {
337    use super::*;
338    use chrono::naive::NaiveDateTime;
339    use chrono::{DateTime, Datelike, NaiveDate, Timelike, Utc};
340
341    impl<TR: TimeRepr> From<NaiveDateTime> for Date<TR> {
342        fn from(d: NaiveDateTime) -> Self {
343            Self {
344                year: u16::try_from(d.year()).unwrap(),
345                month: u8::try_from(d.month()).unwrap(),
346                day: u8::try_from(d.day()).unwrap(),
347                hour: u8::try_from(d.hour()).unwrap(),
348                minute: u8::try_from(d.minute()).unwrap(),
349                second: u8::try_from(d.second()).unwrap(),
350                _pd: std::marker::PhantomData,
351            }
352        }
353    }
354
355    impl<TR: TimeRepr> From<Date<TR>> for NaiveDateTime {
356        fn from(date: Date<TR>) -> Self {
357            NaiveDate::from_ymd_opt(i32::from(date.year), u32::from(date.month), u32::from(date.day))
358                .unwrap()
359                .and_hms_opt(u32::from(date.hour), u32::from(date.minute), u32::from(date.second))
360                .unwrap()
361        }
362    }
363
364    impl<TR: TimeRepr> From<DateTime<Utc>> for Date<TR> {
365        fn from(d: DateTime<Utc>) -> Self {
366            Self {
367                year: u16::try_from(d.year()).unwrap(),
368                month: u8::try_from(d.month()).unwrap(),
369                day: u8::try_from(d.day()).unwrap(),
370                hour: u8::try_from(d.hour()).unwrap(),
371                minute: u8::try_from(d.minute()).unwrap(),
372                second: u8::try_from(d.second()).unwrap(),
373                _pd: std::marker::PhantomData,
374            }
375        }
376    }
377
378    impl<TR: TimeRepr> From<Date<TR>> for DateTime<Utc> {
379        fn from(date: Date<TR>) -> Self {
380            DateTime::<Utc>::from_naive_utc_and_offset(date.into(), Utc)
381        }
382    }
383}