Skip to main content

msgpack_time/
lib.rs

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
10/// Trait providing a function to convert the object to a [UtcDateTime].
11pub trait ToDateTime {
12    /// Converts the object into a [UtcDateTime], or throws an [Error].
13    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/// Errors thrown by [ToDateTime::as_datetime].
89#[derive(Debug)]
90pub enum Error {
91    /// The provided [Value] is not a [Value::Ext].
92    NotExt,
93    /// The provided [Value::Ext] does not represent a Datetime.
94    ExtNotDateTime,
95    /// The provided data is not one of the defined lengths for MessagePack Datetime values.
96    InvalidLength,
97    /// The provided date is out of the acceptible range for a [UtcDateTime].
98    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 {}