#![warn(missing_docs)]
#![doc = include_str!("../README.md")]
use std::fmt::{Debug, Display};
use std::time::{Duration, SystemTime};
use rmpv::Value;
use time::UtcDateTime;
pub trait ToDateTime {
fn as_datetime(&self) -> Result<UtcDateTime, Error>;
}
impl ToDateTime for Value {
fn as_datetime(&self) -> Result<UtcDateTime, Error> {
let ext = self.as_ext().ok_or(Error::NotExt)?;
if ext.0 != -1 {
return Err(Error::ExtNotDateTime);
}
let datetime_bytes = ext.1;
match datetime_bytes.len() {
4 => {
let s = i64::from(u32::from_be_bytes(
datetime_bytes[0..4]
.iter()
.as_slice()
.try_into()
.map_err(|_| Error::InvalidLength)?,
));
UtcDateTime::from_unix_timestamp(s).map_err(|_| Error::OutOfRange)
}
8 => {
let d = u64::from_be_bytes(
datetime_bytes[0..8]
.iter()
.as_slice()
.try_into()
.map_err(|_| Error::InvalidLength)?,
);
let s = d & 0x00000003ffffffff;
let n = d >> 34;
let s_dur = Duration::from_secs(s);
let n_dur = Duration::from_nanos(n);
Ok(UtcDateTime::from(
SystemTime::UNIX_EPOCH
.checked_add(s_dur)
.ok_or(Error::OutOfRange)?
.checked_add(n_dur)
.ok_or(Error::OutOfRange)?,
))
}
12 => {
let n = u32::from_be_bytes(
datetime_bytes[0..4]
.iter()
.as_slice()
.try_into()
.map_err(|_| Error::InvalidLength)?,
);
let s = i64::from_be_bytes(
datetime_bytes[5..12]
.iter()
.as_slice()
.try_into()
.map_err(|_| Error::InvalidLength)?,
);
let s_dur = Duration::from_secs(s.unsigned_abs());
let n_dur = Duration::from_nanos(u64::from(n));
let mut systime = SystemTime::UNIX_EPOCH
.checked_add(n_dur)
.ok_or(Error::OutOfRange)?;
if s.is_negative() {
systime = systime.checked_sub(s_dur).ok_or(Error::OutOfRange)?;
} else {
systime = systime.checked_add(s_dur).ok_or(Error::OutOfRange)?;
}
Ok(UtcDateTime::from(systime))
}
_ => Err(Error::InvalidLength),
}
}
}
#[derive(Debug)]
pub enum Error {
NotExt,
ExtNotDateTime,
InvalidLength,
OutOfRange,
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NotExt => write!(f, "Value is not an Ext"),
Self::ExtNotDateTime => write!(f, "Ext is not a Datetime"),
Self::InvalidLength => {
write!(f, "Ext value is an invalid length for MessagePack Datetime")
}
Self::OutOfRange => write!(f, "Ext value is out of range for UtcDateTime"),
}
}
}
impl std::error::Error for Error {}