use std::{
fmt,
ops::Div,
time::{SystemTime, UNIX_EPOCH},
};
#[cfg(feature = "borsh")]
use borsh::{BorshDeserialize, BorshSerialize};
use newtype_ops::newtype_ops;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Deserialize, Serialize)]
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize,))]
pub struct EpochTime(u64);
impl EpochTime {
pub fn now() -> EpochTime {
EpochTime(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("SystemTime before UNIX EPOCH!")
.as_secs(),
)
}
pub fn from_secs_since_epoch(secs: u64) -> EpochTime {
EpochTime(secs)
}
pub fn as_u64(self) -> u64 {
self.0
}
#[must_use]
pub fn increase(self, seconds: u64) -> EpochTime {
let value = seconds.checked_add(self.0).expect("u64 overflow in timestamp");
EpochTime(value)
}
pub fn checked_add(self, other: EpochTime) -> Option<EpochTime> {
self.0.checked_add(other.0).map(EpochTime)
}
pub fn checked_sub(self, other: EpochTime) -> Option<EpochTime> {
self.0.checked_sub(other.0).map(EpochTime)
}
}
newtype_ops! { [EpochTime] {add sub} {:=} Self Self }
newtype_ops! { [EpochTime] {add sub} {:=} &Self &Self }
newtype_ops! { [EpochTime] {add sub} {:=} Self &Self }
newtype_ops! { [EpochTime] {mul div rem} {:=} Self u64 }
impl Div for EpochTime {
type Output = u64;
fn div(self, rhs: Self) -> Self::Output {
self.0 / rhs.0
}
}
impl fmt::Display for EpochTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<u64> for EpochTime {
fn from(value: u64) -> Self {
EpochTime(value)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn now() {
let a = EpochTime::now();
let b = EpochTime::now();
assert!(a <= b);
}
#[test]
fn as_u64() {
let time = EpochTime::from(1234567);
assert_eq!(time.as_u64(), 1234567);
}
#[test]
fn increase() {
let a = EpochTime::from(1111);
assert_eq!(a.increase(123), EpochTime::from(1234));
}
#[test]
#[should_panic]
fn increase_overflow() {
let a = EpochTime::from(1234);
let _ = a.increase(u64::MAX);
}
#[test]
fn checked_add() {
let a = EpochTime::from(1111);
let b = EpochTime::from_secs_since_epoch(123);
assert_eq!(a.checked_add(b), Some(EpochTime::from(1234)));
let b = EpochTime::from(u64::MAX);
assert_eq!(a.checked_add(b), None);
}
#[test]
fn checked_sub() {
let a = EpochTime::from(1234);
let b = EpochTime::from(123);
assert_eq!(a.checked_sub(b), Some(EpochTime::from(1111)));
assert_eq!(b.checked_sub(a), None);
}
#[test]
fn test_div() {
let a = EpochTime::from(3639);
let b = EpochTime::from(3);
assert_eq!(1213, a / b);
}
#[test]
fn display() {
let time = EpochTime::from(1234567);
assert_eq!("1234567", format!("{}", time));
}
#[test]
fn add_epoch_time() {
assert_eq!(EpochTime::from(1_000) + EpochTime::from(8_000), EpochTime::from(9_000));
assert_eq!(EpochTime::from(15) + EpochTime::from(5), EpochTime::from(20));
}
}