use std::time::SystemTime;
use std::time::{Duration, Instant};
use once_cell::sync::Lazy;
pub(crate) fn not_happening() -> Instant {
const YEARS_100: Duration = Duration::from_secs(60 * 60 * 24 * 365 * 100);
static FUTURE: Lazy<Instant> = Lazy::new(|| Instant::now() + YEARS_100);
*FUTURE
}
static BEGINNING_OF_TIME: Lazy<(Instant, SystemTime)> = Lazy::new(|| {
let now = Instant::now();
let now_sys = SystemTime::now();
let beginning_of_time = {
let mut secs = 3600;
loop {
let dur = Duration::from_secs(secs);
if let Some(v) = now.checked_sub(dur) {
break v;
}
secs -= 1;
if secs == 0 {
panic!("Failed to find a beginning of time instant");
}
}
};
let since_beginning_of_time = Instant::now() - beginning_of_time;
let beginning_of_time_sys = now_sys - since_beginning_of_time;
(beginning_of_time, beginning_of_time_sys)
});
pub fn epoch_to_beginning() -> Duration {
BEGINNING_OF_TIME
.1
.duration_since(SystemTime::UNIX_EPOCH)
.expect("beginning of time to be after epoch")
}
pub(crate) fn already_happened() -> Instant {
BEGINNING_OF_TIME.0
}
pub trait InstantExt {
fn to_unix_duration(&self) -> Duration;
fn to_ntp_duration(&self) -> Duration;
fn from_ntp_64(v: u64) -> Self;
fn as_ntp_64(&self) -> u64;
}
const SECS_1900: u64 = 2_208_988_800;
const MICROS_1900: u64 = SECS_1900 * 1_000_000;
const F32: f64 = 4_294_967_296.0;
impl InstantExt for Instant {
fn to_unix_duration(&self) -> Duration {
if *self < BEGINNING_OF_TIME.0 {
warn!("Time went backwards from beginning_of_time Instant");
}
let duration_since_time_0 = self.duration_since(BEGINNING_OF_TIME.0);
let system_time = BEGINNING_OF_TIME.1 + duration_since_time_0;
system_time
.duration_since(SystemTime::UNIX_EPOCH)
.expect("clock to go forwards from unix epoch")
}
fn to_ntp_duration(&self) -> Duration {
self.to_unix_duration() + Duration::from_micros(MICROS_1900)
}
fn from_ntp_64(v: u64) -> Self {
let secs_ntp = (v as f64) / F32;
let secs_epoch = secs_ntp - SECS_1900 as f64;
let secs_dur = if secs_epoch <= 0.0 {
Duration::ZERO
} else {
Duration::from_secs_f64(secs_epoch)
};
let sys = SystemTime::UNIX_EPOCH + secs_dur;
let since_beginning_of_time = sys
.duration_since(BEGINNING_OF_TIME.1)
.unwrap_or(Duration::ZERO);
BEGINNING_OF_TIME.0 + since_beginning_of_time
}
fn as_ntp_64(&self) -> u64 {
let since_beginning_of_time = self.duration_since(BEGINNING_OF_TIME.0);
let since_epoch = since_beginning_of_time + epoch_to_beginning();
let secs_epoch = since_epoch.as_secs_f64();
let secs_ntp = secs_epoch + SECS_1900 as f64;
(secs_ntp * F32) as u64
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn not_happening_works() {
assert_eq!(not_happening(), not_happening());
assert!(Instant::now() < not_happening());
}
#[test]
fn already_happened_works() {
assert_eq!(already_happened(), already_happened());
assert!(Instant::now() > already_happened());
}
#[test]
fn already_happened_ne() {
assert_ne!(not_happening(), already_happened())
}
#[test]
fn ntp_64_from_to() {
let now = Instant::now();
let ntp = now.as_ntp_64();
let now2 = Instant::from_ntp_64(ntp);
let abs = if now > now2 { now - now2 } else { now2 - now };
assert!(abs < Duration::from_millis(1));
}
#[test]
fn from_ntp_64() {
Instant::from_ntp_64(0);
}
}