use serde::{de, ser, Deserializer, Serializer};
use std::fmt;
pub trait TimeRepr
where
Self: Sized,
{
fn serialize<S>(
date: &Date<Self>,
serializer: S,
) -> Result<<S as ser::Serializer>::Ok, <S as ser::Serializer>::Error>
where
S: ser::Serializer;
fn deserialize<'de, D>(deserializer: D) -> Result<Date<Self>, <D as de::Deserializer<'de>>::Error>
where
D: de::Deserializer<'de>;
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Date<TR: TimeRepr> {
year: u16,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
_pd: std::marker::PhantomData<TR>,
}
impl<TR: TimeRepr> Date<TR> {
pub unsafe fn new_unchecked(year: u16, month: u8, day: u8, hour: u8, minute: u8, second: u8) -> Date<TR> {
Self {
year,
month,
day,
hour,
minute,
second,
_pd: std::marker::PhantomData,
}
}
pub fn new(year: u16, month: u8, day: u8, hour: u8, minute: u8, second: u8) -> Option<Date<TR>> {
if (1..=12).contains(&month) && (1..=32).contains(&day) && hour < 24 && minute < 60 && second < 60 {
Some(Self {
year,
month,
day,
hour,
minute,
second,
_pd: std::marker::PhantomData,
})
} else {
None
}
}
pub fn year(&self) -> u16 {
self.year
}
pub fn month(&self) -> u8 {
self.month
}
pub fn day(&self) -> u8 {
self.day
}
pub fn hour(&self) -> u8 {
self.hour
}
pub fn minute(&self) -> u8 {
self.minute
}
pub fn second(&self) -> u8 {
self.second
}
}
impl<TR: TimeRepr> ser::Serialize for Date<TR> {
fn serialize<S>(&self, serializer: S) -> Result<<S as ser::Serializer>::Ok, <S as ser::Serializer>::Error>
where
S: ser::Serializer,
{
TR::serialize(self, serializer)
}
}
impl<'de, TR: TimeRepr> de::Deserialize<'de> for Date<TR> {
fn deserialize<D>(deserializer: D) -> Result<Self, <D as de::Deserializer<'de>>::Error>
where
D: de::Deserializer<'de>,
{
TR::deserialize(deserializer)
}
}
trait DateDigitReader {
fn read_digit(&self, idx: usize) -> u8;
#[inline]
fn read_and_merge_with_next(&self, idx: usize) -> u8 {
self.read_digit(idx) * 10 + self.read_digit(idx + 1)
}
}
impl DateDigitReader for [u8] {
#[inline]
fn read_digit(&self, idx: usize) -> u8 {
self[idx] & 0x0F
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct UTCTimeRepr;
pub type UTCTime = Date<UTCTimeRepr>;
impl TimeRepr for UTCTimeRepr {
fn serialize<S>(date: &Date<UTCTimeRepr>, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: ser::Serializer,
{
let mut encoded = [
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A,
];
let year = if date.year() >= 2000 {
date.year() - 2000
} else {
date.year() - 1900
};
encoded[0] |= (year / 10) as u8;
encoded[1] |= (year % 10) as u8;
encoded[2] |= date.month() / 10;
encoded[3] |= date.month() % 10;
encoded[4] |= date.day() / 10;
encoded[5] |= date.day() % 10;
encoded[6] |= date.hour() / 10;
encoded[7] |= date.hour() % 10;
encoded[8] |= date.minute() / 10;
encoded[9] |= date.minute() % 10;
encoded[10] |= date.second() / 10;
encoded[11] |= date.second() % 10;
serializer.serialize_bytes(&encoded)
}
fn deserialize<'de, D>(deserializer: D) -> Result<Date<UTCTimeRepr>, <D as Deserializer<'de>>::Error>
where
D: de::Deserializer<'de>,
{
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = Date<UTCTimeRepr>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a valid buffer representing an Asn1 UTCTime")
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: de::Error,
{
if v.len() != 13 {
return Err(E::invalid_value(
de::Unexpected::Other("unsupported date format"),
&"a valid buffer representing an Asn1 UTCTime (exactly 13 bytes required)",
));
}
let yyyy = {
let yy = v.read_and_merge_with_next(0) as u16;
if yy >= 50 {
1900 + yy
} else {
2000 + yy
}
};
let month = v.read_and_merge_with_next(2);
let day = v.read_and_merge_with_next(4);
let hour = v.read_and_merge_with_next(6);
let minute = v.read_and_merge_with_next(8);
let second = v.read_and_merge_with_next(10);
let dt = Date::new(yyyy, month, day, hour, minute, second).ok_or_else(|| {
E::invalid_value(
de::Unexpected::Other("invalid parameters provided to Date constructor"),
&"valid parameters for Date",
)
})?;
Ok(dt)
}
}
deserializer.deserialize_bytes(Visitor)
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct GeneralizedTimeRepr;
pub type GeneralizedTime = Date<GeneralizedTimeRepr>;
impl TimeRepr for GeneralizedTimeRepr {
fn serialize<S>(
date: &Date<GeneralizedTimeRepr>,
serializer: S,
) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: ser::Serializer,
{
let mut encoded = [
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A,
];
encoded[0] |= (date.year() / 1000) as u8;
encoded[1] |= ((date.year() % 1000) / 100) as u8;
encoded[2] |= ((date.year() % 100) / 10) as u8;
encoded[3] |= (date.year() % 10) as u8;
encoded[4] |= date.month() / 10;
encoded[5] |= date.month() % 10;
encoded[6] |= date.day() / 10;
encoded[7] |= date.day() % 10;
encoded[8] |= date.hour() / 10;
encoded[9] |= date.hour() % 10;
encoded[10] |= date.minute() / 10;
encoded[11] |= date.minute() % 10;
encoded[12] |= date.second() / 10;
encoded[13] |= date.second() % 10;
serializer.serialize_bytes(&encoded)
}
fn deserialize<'de, D>(deserializer: D) -> Result<Date<GeneralizedTimeRepr>, <D as Deserializer<'de>>::Error>
where
D: de::Deserializer<'de>,
{
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = Date<GeneralizedTimeRepr>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a valid buffer representing an Asn1 GeneralizedTime")
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: de::Error,
{
if v.len() != 15 {
return Err(E::invalid_value(
de::Unexpected::Other("unsupported date format"),
&"a valid buffer representing an Asn1 GeneralizedTime (exactly 15 bytes required)",
));
}
let yyyy = v.read_and_merge_with_next(0) as u16 * 100 + v.read_and_merge_with_next(2) as u16;
let month = v.read_and_merge_with_next(4);
let day = v.read_and_merge_with_next(6);
let hour = v.read_and_merge_with_next(8);
let minute = v.read_and_merge_with_next(10);
let second = v.read_and_merge_with_next(12);
let dt = Date::new(yyyy, month, day, hour, minute, second).ok_or_else(|| {
E::invalid_value(
de::Unexpected::Other("invalid parameters provided to Date constructor"),
&"valid parameters for Date",
)
})?;
Ok(dt)
}
}
deserializer.deserialize_bytes(Visitor)
}
}
#[cfg(feature = "time_conversion")]
mod time_convert {
use super::*;
use time::{OffsetDateTime, PrimitiveDateTime};
impl<TR: TimeRepr> From<PrimitiveDateTime> for Date<TR> {
fn from(d: PrimitiveDateTime) -> Self {
Self::from(d.assume_utc())
}
}
impl<TR: TimeRepr> TryFrom<Date<TR>> for PrimitiveDateTime {
type Error = time::error::ComponentRange;
fn try_from(d: Date<TR>) -> Result<Self, Self::Error> {
let date = time::Date::from_calendar_date(i32::from(d.year), time::Month::try_from(d.month)?, d.day)?;
let time = time::Time::from_hms(d.hour, d.minute, d.second)?;
Ok(Self::new(date, time))
}
}
impl<TR: TimeRepr> From<OffsetDateTime> for Date<TR> {
fn from(d: OffsetDateTime) -> Self {
Self {
year: u16::try_from(d.year()).unwrap(),
month: u8::from(d.month()),
day: d.day(),
hour: d.hour(),
minute: d.minute(),
second: d.second(),
_pd: std::marker::PhantomData,
}
}
}
impl<TR: TimeRepr> TryFrom<Date<TR>> for OffsetDateTime {
type Error = time::error::ComponentRange;
fn try_from(d: Date<TR>) -> Result<Self, Self::Error> {
Ok(PrimitiveDateTime::try_from(d)?.assume_utc())
}
}
}
#[cfg(feature = "chrono_conversion")]
mod chrono_convert {
use super::*;
use chrono::naive::NaiveDateTime;
use chrono::{DateTime, Datelike, NaiveDate, Timelike, Utc};
impl<TR: TimeRepr> From<NaiveDateTime> for Date<TR> {
fn from(d: NaiveDateTime) -> Self {
Self {
year: u16::try_from(d.year()).unwrap(),
month: u8::try_from(d.month()).unwrap(),
day: u8::try_from(d.day()).unwrap(),
hour: u8::try_from(d.hour()).unwrap(),
minute: u8::try_from(d.minute()).unwrap(),
second: u8::try_from(d.second()).unwrap(),
_pd: std::marker::PhantomData,
}
}
}
impl<TR: TimeRepr> From<Date<TR>> for NaiveDateTime {
fn from(date: Date<TR>) -> Self {
NaiveDate::from_ymd_opt(i32::from(date.year), u32::from(date.month), u32::from(date.day))
.unwrap()
.and_hms_opt(u32::from(date.hour), u32::from(date.minute), u32::from(date.second))
.unwrap()
}
}
impl<TR: TimeRepr> From<DateTime<Utc>> for Date<TR> {
fn from(d: DateTime<Utc>) -> Self {
Self {
year: u16::try_from(d.year()).unwrap(),
month: u8::try_from(d.month()).unwrap(),
day: u8::try_from(d.day()).unwrap(),
hour: u8::try_from(d.hour()).unwrap(),
minute: u8::try_from(d.minute()).unwrap(),
second: u8::try_from(d.second()).unwrap(),
_pd: std::marker::PhantomData,
}
}
}
impl<TR: TimeRepr> From<Date<TR>> for DateTime<Utc> {
fn from(date: Date<TR>) -> Self {
DateTime::<Utc>::from_utc(date.into(), Utc)
}
}
}