#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct JsInstant {
inner: std::time::Duration,
}
impl JsInstant {
#[must_use]
pub fn new(secs: u64, nanos: u32) -> Self {
let inner = std::time::Duration::new(secs, nanos);
Self::new_unchecked(inner)
}
#[must_use]
fn new_unchecked(inner: std::time::Duration) -> Self {
Self { inner }
}
#[must_use]
pub fn millis_since_epoch(&self) -> u64 {
self.inner.as_millis() as u64
}
#[must_use]
pub fn nanos_since_epoch(&self) -> u128 {
self.inner.as_nanos()
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct JsDuration {
inner: std::time::Duration,
}
impl JsDuration {
#[must_use]
pub fn from_millis(millis: u64) -> Self {
Self {
inner: std::time::Duration::from_millis(millis),
}
}
#[must_use]
pub fn as_millis(&self) -> u64 {
self.inner.as_millis() as u64
}
#[must_use]
pub fn as_secs(&self) -> u64 {
self.inner.as_secs()
}
#[must_use]
pub fn as_nanos(&self) -> u128 {
self.inner.as_nanos()
}
}
impl From<std::time::Duration> for JsDuration {
fn from(duration: std::time::Duration) -> Self {
Self { inner: duration }
}
}
impl From<JsDuration> for std::time::Duration {
fn from(duration: JsDuration) -> Self {
duration.inner
}
}
macro_rules! impl_duration_ops {
($($trait:ident $trait_fn:ident),*) => {
$(
impl std::ops::$trait for JsDuration {
type Output = JsDuration;
#[inline]
fn $trait_fn(self, rhs: JsDuration) -> Self::Output {
Self {
inner: std::ops::$trait::$trait_fn(self.inner, rhs.inner)
}
}
}
impl std::ops::$trait<JsDuration> for JsInstant {
type Output = JsInstant;
#[inline]
fn $trait_fn(self, rhs: JsDuration) -> Self::Output {
Self {
inner: std::ops::$trait::$trait_fn(self.inner, rhs.inner)
}
}
}
)*
};
}
impl_duration_ops!(Add add, Sub sub);
impl std::ops::Sub for JsInstant {
type Output = JsDuration;
#[inline]
fn sub(self, rhs: JsInstant) -> Self::Output {
JsDuration {
inner: self.inner - rhs.inner,
}
}
}
pub trait Clock {
fn now(&self) -> JsInstant;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct StdClock;
impl Clock for StdClock {
fn now(&self) -> JsInstant {
let now = std::time::SystemTime::now();
let duration = now
.duration_since(std::time::UNIX_EPOCH)
.expect("System clock is before Unix epoch");
JsInstant::new_unchecked(duration)
}
}
#[derive(Debug, Clone, Default)]
pub struct FixedClock(std::cell::RefCell<u64>);
impl FixedClock {
#[must_use]
pub fn from_millis(millis: u64) -> Self {
Self(std::cell::RefCell::new(millis))
}
pub fn forward(&self, millis: u64) {
*self.0.borrow_mut() += millis;
}
}
impl Clock for FixedClock {
fn now(&self) -> JsInstant {
let millis = *self.0.borrow();
JsInstant::new_unchecked(std::time::Duration::new(
millis / 1000,
((millis % 1000) * 1_000_000) as u32,
))
}
}
#[test]
fn basic() {
let now = StdClock.now();
assert!(now.millis_since_epoch() > 0);
assert!(now.nanos_since_epoch() > 0);
let duration = JsDuration::from_millis(1000);
let later = now + duration;
assert!(later > now);
let earlier = now - duration;
assert!(earlier < now);
let diff = later - earlier;
assert_eq!(diff.as_millis(), 2000);
let fixed = FixedClock::from_millis(0);
let now2 = fixed.now();
assert_eq!(now2.millis_since_epoch(), 0);
assert!(now2 < now);
fixed.forward(1000);
let now3 = fixed.now();
assert_eq!(now3.millis_since_epoch(), 1000);
assert!(now3 > now2);
fixed.forward(u64::MAX - 1000);
let now4 = fixed.now();
assert_eq!(now4.millis_since_epoch(), u64::MAX);
assert!(now4 > now3);
}