use std::future::Future;
use std::pin::Pin;
use std::time::Duration;
use chrono::{DateTime, TimeZone, Utc};
use crate::tools::time::{DurationMillis, TimeMillis};
pub trait TimeProvider: Send + Sync {
fn current_time_millis(&self) -> TimeMillis;
fn current_time_str(&self) -> String {
self.current_time_millis().to_string()
}
fn current_datetime(&self) -> DateTime<Utc> {
let millis = self.current_time_millis();
Utc.timestamp_opt(millis.as_secs(), millis.part_nanos() as u32).unwrap()
}
fn sleep(&self, duration: Duration) -> Pin<Box<dyn Future<Output = ()> + Send>>;
fn sleep_millis(&self, millis: DurationMillis) -> Pin<Box<dyn Future<Output = ()> + Send>> {
self.sleep(Duration::from(millis))
}
}
#[derive(Default, Clone)]
pub struct RealTimeProvider;
impl TimeProvider for RealTimeProvider {
fn current_time_millis(&self) -> TimeMillis {
let now: DateTime<Utc> = Utc::now();
TimeMillis(now.timestamp_millis())
}
fn sleep(&self, duration: Duration) -> Pin<Box<dyn Future<Output = ()> + Send>> {
Box::pin(tokio::time::sleep(duration))
}
}
#[derive(Clone)]
pub struct ScaledTimeProvider {
scale_factor: f64,
start_real_time: i64,
}
impl ScaledTimeProvider {
pub fn new(scale_factor: f64) -> Self {
Self {
scale_factor,
start_real_time: Utc::now().timestamp_millis(),
}
}
}
impl TimeProvider for ScaledTimeProvider {
fn current_time_millis(&self) -> TimeMillis {
let real_now = Utc::now().timestamp_millis();
let real_elapsed = real_now.saturating_sub(self.start_real_time);
let scaled_elapsed = (real_elapsed as f64 * self.scale_factor) as i64;
TimeMillis(scaled_elapsed)
}
fn sleep(&self, duration: Duration) -> Pin<Box<dyn Future<Output = ()> + Send>> {
let scaled_duration = Duration::from_secs_f64(duration.as_secs_f64() / self.scale_factor);
Box::pin(tokio::time::sleep(scaled_duration))
}
}