use std::fmt;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
#[serde(transparent)]
pub struct UnixMillis(pub i64);
impl UnixMillis {
pub const EPOCH: UnixMillis = UnixMillis(0);
pub const fn new(millis: i64) -> Self {
Self(millis)
}
pub const fn get(self) -> i64 {
self.0
}
pub fn now() -> Self {
Self::from_system_time(SystemTime::now())
}
pub fn from_system_time(time: SystemTime) -> Self {
match time.duration_since(UNIX_EPOCH) {
Ok(after) => Self(i64::try_from(after.as_millis()).unwrap_or(i64::MAX)),
Err(before) => Self(i64::try_from(before.duration().as_millis()).map(|m| -m).unwrap_or(i64::MIN)),
}
}
pub fn to_system_time(self) -> Option<SystemTime> {
let magnitude = Duration::from_millis(self.0.unsigned_abs());
if self.0 >= 0 {
UNIX_EPOCH.checked_add(magnitude)
} else {
UNIX_EPOCH.checked_sub(magnitude)
}
}
pub const fn saturating_add_millis(self, millis: i64) -> Self {
Self(self.0.saturating_add(millis))
}
}
impl fmt::Display for UnixMillis {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl From<i64> for UnixMillis {
fn from(millis: i64) -> Self {
Self(millis)
}
}
impl From<UnixMillis> for i64 {
fn from(time: UnixMillis) -> Self {
time.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unix_millis() {
assert_eq!(serde_json::to_string(&UnixMillis(1_790_000_000_000)).ok().as_deref(), Some("1790000000000"));
assert_eq!(UnixMillis::from_system_time(UNIX_EPOCH + Duration::from_millis(1500)), UnixMillis(1500));
assert_eq!(UnixMillis::from_system_time(UNIX_EPOCH - Duration::from_millis(1500)), UnixMillis(-1500));
assert_eq!(UnixMillis(1500).to_system_time(), Some(UNIX_EPOCH + Duration::from_millis(1500)));
assert_eq!(UnixMillis(-1500).to_system_time(), Some(UNIX_EPOCH - Duration::from_millis(1500)));
assert!(UnixMillis::now() > UnixMillis(1_700_000_000_000));
assert_eq!(UnixMillis(i64::MAX).saturating_add_millis(5), UnixMillis(i64::MAX));
assert_eq!(UnixMillis(7).to_string(), "7");
}
}