1use std::time::{Duration, SystemTime};
2
3use crate::{Error, Result, Value, tag};
4
5#[derive(Debug, Clone, Copy, PartialEq)]
6enum Inner {
7 Int(u64),
8 Float(f64),
9}
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
23pub struct EpochTime(Inner);
24
25impl Eq for Inner {} impl Ord for Inner {
28 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
29 match (self, other) {
30 (Self::Int(a), Self::Int(b)) => a.cmp(b),
31 (Self::Float(a), Self::Float(b)) => a.total_cmp(b),
32 (Self::Int(a), Self::Float(b)) => (*a as f64).total_cmp(b),
35 (Self::Float(a), Self::Int(b)) => a.total_cmp(&(*b as f64)),
36 }
37 }
38}
39
40impl PartialOrd for Inner {
41 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
42 Some(self.cmp(other))
43 }
44}
45
46impl std::hash::Hash for Inner {
47 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
48 core::mem::discriminant(self).hash(state);
49 match self {
50 Inner::Int(x) => x.hash(state),
51 Inner::Float(x) => x.to_bits().hash(state),
52 }
53 }
54}
55
56impl From<EpochTime> for Value {
57 fn from(value: EpochTime) -> Self {
58 match value.0 {
59 Inner::Int(int) => Self::tag(tag::EPOCH_TIME, int),
60 Inner::Float(float) => Self::tag(tag::EPOCH_TIME, float),
61 }
62 }
63}
64
65impl TryFrom<SystemTime> for EpochTime {
66 type Error = Error;
67
68 fn try_from(value: SystemTime) -> Result<Self> {
69 let time = value
70 .duration_since(SystemTime::UNIX_EPOCH)
71 .or(Err(Error::InvalidValue))?;
72
73 if time > Duration::from_secs(253402300799) {
74 Err(Error::InvalidValue)
75 } else if time.subsec_nanos() == 0 {
76 Ok(Self(Inner::Int(time.as_secs())))
77 } else {
78 Ok(Self(Inner::Float(time.as_secs_f64())))
79 }
80 }
81}
82
83fn from_int<T: TryInto<u64>>(value: T) -> Result<EpochTime> {
84 let value = value.try_into().or(Err(Error::InvalidValue))?;
85
86 if (0..=253402300799).contains(&value) {
87 Ok(EpochTime(Inner::Int(value)))
88 } else {
89 Err(Error::InvalidValue)
90 }
91}
92
93macro_rules! try_from {
94 ($type:ty) => {
95 impl TryFrom<$type> for EpochTime {
96 type Error = Error;
97 fn try_from(value: $type) -> Result<Self> {
98 from_int(value)
99 }
100 }
101 };
102}
103
104try_from!(u8);
105try_from!(u16);
106try_from!(u32);
107try_from!(u64);
108try_from!(u128);
109try_from!(usize);
110
111try_from!(i8);
112try_from!(i16);
113try_from!(i32);
114try_from!(i64);
115try_from!(i128);
116try_from!(isize);
117
118impl TryFrom<f32> for EpochTime {
119 type Error = Error;
120
121 fn try_from(value: f32) -> Result<Self> {
122 Self::try_from(f64::from(value))
123 }
124}
125impl TryFrom<f64> for EpochTime {
126 type Error = Error;
127
128 fn try_from(value: f64) -> Result<Self> {
129 if value.is_finite() && (0.0..=253402300799.0).contains(&value) {
130 Ok(Self(Inner::Float(value)))
131 } else {
132 Err(Error::InvalidValue)
133 }
134 }
135}