use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Timestamp {
nanos: i64,
}
fn origin() -> Instant {
use std::sync::OnceLock;
static ORIGIN: OnceLock<Instant> = OnceLock::new();
*ORIGIN.get_or_init(Instant::now)
}
impl Timestamp {
pub fn now() -> Self {
let elapsed = origin().elapsed();
Self {
nanos: elapsed.as_nanos().min(i64::MAX as u128) as i64,
}
}
pub const fn zero() -> Self {
Self { nanos: 0 }
}
pub const fn from_nanos(n: i64) -> Self {
Self { nanos: n }
}
pub fn from_secs_f64(s: f64) -> Self {
Self {
nanos: (s * 1e9) as i64,
}
}
pub const fn as_nanos(self) -> i64 {
self.nanos
}
pub fn to_secs(self) -> f64 {
self.nanos as f64 / 1e9
}
pub fn to_ms(self) -> f64 {
self.nanos as f64 / 1e6
}
pub fn is_valid(self) -> bool {
self.nanos > 0
}
}
impl std::ops::Sub for Timestamp {
type Output = Timestamp;
fn sub(self, rhs: Self) -> Self {
Self {
nanos: self.nanos.saturating_sub(rhs.nanos),
}
}
}
impl std::ops::Add for Timestamp {
type Output = Timestamp;
fn add(self, rhs: Self) -> Self {
Self {
nanos: self.nanos.saturating_add(rhs.nanos),
}
}
}
impl std::ops::SubAssign for Timestamp {
fn sub_assign(&mut self, rhs: Self) {
self.nanos = self.nanos.saturating_sub(rhs.nanos);
}
}
impl std::ops::AddAssign for Timestamp {
fn add_assign(&mut self, rhs: Self) {
self.nanos = self.nanos.saturating_add(rhs.nanos);
}
}
pub struct Stopwatch {
start: Timestamp,
elapsed: Timestamp,
running: bool,
}
impl Default for Stopwatch {
fn default() -> Self {
Self::new()
}
}
impl Stopwatch {
pub fn new() -> Self {
Self {
start: Timestamp::zero(),
elapsed: Timestamp::zero(),
running: false,
}
}
pub fn start(&mut self) {
if !self.running {
self.start = Timestamp::now();
self.running = true;
}
}
pub fn stop(&mut self) {
if self.running {
self.elapsed += Timestamp::now() - self.start;
self.running = false;
}
}
pub fn reset(&mut self) {
self.elapsed = Timestamp::zero();
self.running = false;
}
pub fn restart(&mut self) {
self.reset();
self.start();
}
pub fn elapsed(&self) -> Timestamp {
if self.running {
self.elapsed + (Timestamp::now() - self.start)
} else {
self.elapsed
}
}
pub fn elapsed_sec(&self) -> f64 {
self.elapsed().to_secs()
}
pub fn elapsed_ms(&self) -> f64 {
self.elapsed().to_ms()
}
pub fn is_running(&self) -> bool {
self.running
}
}
pub struct ScopedTimer<'a> {
output: &'a mut f64,
start: Timestamp,
}
impl<'a> ScopedTimer<'a> {
pub fn new(output: &'a mut f64) -> Self {
Self {
output,
start: Timestamp::now(),
}
}
}
impl Drop for ScopedTimer<'_> {
fn drop(&mut self) {
*self.output = (Timestamp::now() - self.start).to_secs();
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Datetime {
unix_secs: i64,
}
impl Datetime {
pub fn now() -> Self {
let secs = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or(Duration::ZERO)
.as_secs();
Self {
unix_secs: secs as i64,
}
}
pub const fn from_unix_secs(secs: i64) -> Self {
Self { unix_secs: secs }
}
pub const fn as_unix_secs(self) -> i64 {
self.unix_secs
}
}
impl std::fmt::Display for Datetime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = self.unix_secs.unsigned_abs();
let secs = s % 60;
let mins = (s / 60) % 60;
let hours = (s / 3600) % 24;
let days = s / 86400;
let (y, m, d) = civil_from_days(days);
write!(f, "{y:04}-{m:02}-{d:02} {hours:02}:{mins:02}:{secs:02}")
}
}
fn civil_from_days(z: u64) -> (u32, u32, u32) {
let z = z as i64 + 719_468;
let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
let doe = (z - era * 146_097) as u32;
let yoe = (doe - doe / 1_460 + doe / 36_524 - doe / 146_096) / 365;
let y = yoe as i64 + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = doy - (153 * mp + 2) / 5 + 1;
let m = if mp < 10 { mp + 3 } else { mp - 9 };
let y = if m <= 2 { y + 1 } else { y };
(y as u32, m, d)
}