use std::cmp::Ordering;
use std::fmt::{self, Display, Formatter, LowerHex};
use time::{UtcDateTime, macros::utc_datetime};
const EPOCH: UtcDateTime = utc_datetime!(2025-06-01 00:00:00);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Timestamp {
seconds: u64,
}
impl Timestamp {
pub fn new() -> Self {
let now = UtcDateTime::now();
let diff: u64 = (now - EPOCH)
.whole_seconds()
.try_into()
.expect("clock is before timestamp epoch; your clock is likely wrong.");
Self::from_seconds(diff)
}
pub fn to_bytes(self) -> [u8; 8] {
self.as_seconds().to_be_bytes()
}
pub fn from_bytes(bytes: [u8; 8]) -> Self {
u64::from_be_bytes(bytes).into()
}
pub fn from_seconds(seconds: u64) -> Self {
Self { seconds }
}
pub fn into_seconds(self) -> u64 {
self.seconds
}
pub fn as_seconds(&self) -> &u64 {
&self.seconds
}
pub fn try_from_hex(s: &str) -> Result<Self, std::num::ParseIntError> {
u64::from_str_radix(s, 16).map(Self::from_seconds)
}
}
impl Default for Timestamp {
fn default() -> Self {
Self::new()
}
}
impl From<u64> for Timestamp {
fn from(sec: u64) -> Self {
Timestamp::from_seconds(sec)
}
}
impl From<[u8; 8]> for Timestamp {
fn from(bytes: [u8; 8]) -> Self {
Self::from_bytes(bytes)
}
}
impl TryFrom<&[u8]> for Timestamp {
type Error = std::array::TryFromSliceError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
let bytes: [u8; 8] = slice.try_into()?;
Ok(bytes.into())
}
}
impl TryFrom<Vec<u8>> for Timestamp {
type Error = std::array::TryFromSliceError;
fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
vec.as_slice().try_into()
}
}
impl From<Timestamp> for u64 {
fn from(ts: Timestamp) -> Self {
ts.into_seconds()
}
}
impl From<Timestamp> for [u8; 8] {
fn from(ts: Timestamp) -> Self {
ts.to_bytes()
}
}
impl From<Timestamp> for Vec<u8> {
fn from(ts: Timestamp) -> Self {
ts.to_bytes().into()
}
}
impl LowerHex for Timestamp {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:x}", self.as_seconds())?;
Ok(())
}
}
impl Display for Timestamp {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
LowerHex::fmt(&self, f)
}
}
impl Ord for Timestamp {
fn cmp(&self, other: &Self) -> Ordering {
self.as_seconds().cmp(other.as_seconds())
}
}
impl PartialOrd for Timestamp {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}