use std::fmt;
use std::ops::{Add, AddAssign, Sub, SubAssign};
use std::time::{SystemTime, Duration, UNIX_EPOCH};
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Default, Debug)]
pub struct Epoch32<const Y: u32>(u32);
impl<const Y: u32> Epoch32<Y> {
const DIFF_SECS: u64 = (Y as u64 - 1970) * 365 * 86400;
fn try_from_raw_u64(n: u64) -> Option<Self> {
match u32::try_from(n) {
Ok(n) => Some(Epoch32(n)),
Err(_) => None,
}
}
fn try_from_sub_u64(n1: u64, n2: u64) -> Option<Self> {
if n1 >= n2 {
Self::try_from_raw_u64(n1 - n2)
} else {
None
}
}
pub fn try_from_unix_epoch(u: u64) -> Option<Self> {
Self::try_from_sub_u64(u, Self::DIFF_SECS)
}
pub fn to_unix_epoch(self) -> u64 {
self.0 as u64 + Self::DIFF_SECS
}
pub fn now() -> Self {
Self::try_from(&SystemTime::now()).expect("too big timestamp")
}
pub fn duration_since(self, earlier: Self) -> Option<Duration> {
if self.0 >= earlier.0 {
Some(Duration::from_secs((self.0 - earlier.0) as u64))
} else {
None
}
}
pub fn elapsed(self) -> Option<Duration> {
Self::now().duration_since(self)
}
pub fn checked_add(self, dur: Duration) -> Option<Self> {
Self::try_from_raw_u64(self.0 as u64 + dur.as_secs())
}
pub fn checked_sub(self, dur: Duration) -> Option<Self> {
Self::try_from_sub_u64(self.0 as u64, dur.as_secs())
}
}
impl<const Y: u32> fmt::Display for Epoch32<Y> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{}", self.to_unix_epoch())
}
}
impl<const Y: u32> TryFrom<&SystemTime> for Epoch32<Y> {
type Error = ();
fn try_from(st: &SystemTime) -> Result<Self, Self::Error> {
match st.duration_since(UNIX_EPOCH) {
Ok(n) => Self::try_from_unix_epoch(n.as_secs()).ok_or(()),
Err(_) => Err(()),
}
}
}
impl<const Y: u32> Into<SystemTime> for Epoch32<Y> {
fn into(self) -> SystemTime {
UNIX_EPOCH + Duration::from_secs(self.to_unix_epoch())
}
}
impl<const Y: u32> Add<Duration> for Epoch32<Y> {
type Output = Self;
fn add(self, dur: Duration) -> Self {
self.checked_add(dur).expect("overflow when adding duration to instant")
}
}
impl<const Y: u32> Sub<Duration> for Epoch32<Y> {
type Output = Self;
fn sub(self, dur: Duration) -> Self {
self.checked_sub(dur).expect("overflow when subing duration to instant")
}
}
impl<const Y: u32> AddAssign<Duration> for Epoch32<Y> {
fn add_assign(&mut self, other: Duration) {
*self = *self + other;
}
}
impl<const Y: u32> SubAssign<Duration> for Epoch32<Y> {
fn sub_assign(&mut self, other: Duration) {
*self = *self - other;
}
}
#[cfg(feature="serde")]
use serde::{Serialize, Deserialize, Serializer, Deserializer};
#[cfg(feature="serde")]
impl<const Y: u32> Serialize for Epoch32<Y> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
serializer.serialize_u64(self.to_unix_epoch())
}
}
#[cfg(feature="serde")]
impl<'de, const Y: u32> Deserialize<'de> for Epoch32<Y> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>,
{
let n = u64::deserialize(deserializer)?;
Epoch32::<Y>::try_from_unix_epoch(n)
.ok_or(serde::de::Error::custom("Epoch32 overflow"))
}
}