1#![warn(missing_docs)]
2#![doc = include_str!("../README.md")]
3
4use std::fmt::{Debug, Display};
5use std::time::{Duration, SystemTime};
6
7use rmpv::Value;
8use time::UtcDateTime;
9
10pub trait ToDateTime {
12 fn as_datetime(&self) -> Result<UtcDateTime, Error>;
14}
15
16impl ToDateTime for Value {
17 fn as_datetime(&self) -> Result<UtcDateTime, Error> {
18 let ext = self.as_ext().ok_or(Error::NotExt)?;
19 if ext.0 != -1 {
20 return Err(Error::ExtNotDateTime);
21 }
22
23 let datetime_bytes = ext.1;
24
25 match datetime_bytes.len() {
26 4 => {
27 let s = i64::from(u32::from_be_bytes(
28 datetime_bytes[0..4]
29 .iter()
30 .as_slice()
31 .try_into()
32 .map_err(|_| Error::InvalidLength)?,
33 ));
34 UtcDateTime::from_unix_timestamp(s).map_err(|_| Error::OutOfRange)
35 }
36 8 => {
37 let d = u64::from_be_bytes(
38 datetime_bytes[0..8]
39 .iter()
40 .as_slice()
41 .try_into()
42 .map_err(|_| Error::InvalidLength)?,
43 );
44 let s = d & 0x00000003ffffffff;
45 let n = d >> 34;
46 let s_dur = Duration::from_secs(s);
47 let n_dur = Duration::from_nanos(n);
48 Ok(UtcDateTime::from(
49 SystemTime::UNIX_EPOCH
50 .checked_add(s_dur)
51 .ok_or(Error::OutOfRange)?
52 .checked_add(n_dur)
53 .ok_or(Error::OutOfRange)?,
54 ))
55 }
56 12 => {
57 let n = u32::from_be_bytes(
58 datetime_bytes[0..4]
59 .iter()
60 .as_slice()
61 .try_into()
62 .map_err(|_| Error::InvalidLength)?,
63 );
64 let s = i64::from_be_bytes(
65 datetime_bytes[5..12]
66 .iter()
67 .as_slice()
68 .try_into()
69 .map_err(|_| Error::InvalidLength)?,
70 );
71 let s_dur = Duration::from_secs(s.unsigned_abs());
72 let n_dur = Duration::from_nanos(u64::from(n));
73 let mut systime = SystemTime::UNIX_EPOCH
74 .checked_add(n_dur)
75 .ok_or(Error::OutOfRange)?;
76 if s.is_negative() {
77 systime = systime.checked_sub(s_dur).ok_or(Error::OutOfRange)?;
78 } else {
79 systime = systime.checked_add(s_dur).ok_or(Error::OutOfRange)?;
80 }
81 Ok(UtcDateTime::from(systime))
82 }
83 _ => Err(Error::InvalidLength),
84 }
85 }
86}
87
88#[derive(Debug)]
90pub enum Error {
91 NotExt,
93 ExtNotDateTime,
95 InvalidLength,
97 OutOfRange,
99}
100impl Display for Error {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 match self {
103 Self::NotExt => write!(f, "Value is not an Ext"),
104 Self::ExtNotDateTime => write!(f, "Ext is not a Datetime"),
105 Self::InvalidLength => {
106 write!(f, "Ext value is an invalid length for MessagePack Datetime")
107 }
108 Self::OutOfRange => write!(f, "Ext value is out of range for UtcDateTime"),
109 }
110 }
111}
112impl std::error::Error for Error {}