use chrono::{DateTime, Utc};
use std::time::{Duration, Instant, SystemTime};
pub struct Timer {
start: Instant,
}
impl Timer {
pub fn new() -> Self {
Self {
start: Instant::now(),
}
}
pub fn elapsed(&self) -> Duration {
self.start.elapsed()
}
pub fn elapsed_ms(&self) -> u64 {
self.elapsed().as_millis() as u64
}
pub fn elapsed_micros(&self) -> u64 {
self.elapsed().as_micros() as u64
}
pub fn elapsed_nanos(&self) -> u64 {
self.elapsed().as_nanos() as u64
}
pub fn reset(&mut self) {
self.start = Instant::now();
}
}
impl Default for Timer {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct Profiler {
operations: std::collections::HashMap<String, Vec<Duration>>,
current_operation: Option<(String, Instant)>,
}
impl Profiler {
pub fn new() -> Self {
Self {
operations: std::collections::HashMap::new(),
current_operation: None,
}
}
pub fn start_operation(&mut self, name: &str) {
if let Some((prev_name, start)) = self.current_operation.take() {
let duration = start.elapsed();
self.operations
.entry(prev_name)
.or_insert_with(Vec::new)
.push(duration);
}
self.current_operation = Some((name.to_string(), Instant::now()));
}
pub fn stop_operation(&mut self) {
if let Some((name, start)) = self.current_operation.take() {
let duration = start.elapsed();
self.operations
.entry(name)
.or_insert_with(Vec::new)
.push(duration);
}
}
pub fn get_stats(&self, operation: &str) -> Option<OperationStats> {
self.operations.get(operation).map(|durations| {
let mut sorted = durations.clone();
sorted.sort();
let count = sorted.len();
let total: Duration = sorted.iter().sum();
let average = if count > 0 {
Duration::from_nanos(total.as_nanos() as u64 / count as u64)
} else {
Duration::ZERO
};
let min = sorted.first().copied().unwrap_or_default();
let max = sorted.last().copied().unwrap_or_default();
let median = if count % 2 == 0 {
let mid = count / 2;
Duration::from_nanos(
((sorted[mid - 1].as_nanos() + sorted[mid].as_nanos()) / 2) as u64,
)
} else {
sorted[count / 2]
};
OperationStats {
count,
total,
average,
min,
max,
median,
}
})
}
pub fn operation_names(&self) -> Vec<String> {
self.operations.keys().cloned().collect()
}
pub fn clear(&mut self) {
self.operations.clear();
self.current_operation = None;
}
}
impl Default for Profiler {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct OperationStats {
pub count: usize,
pub total: Duration,
pub average: Duration,
pub min: Duration,
pub max: Duration,
pub median: Duration,
}
impl OperationStats {
pub fn total_ms(&self) -> f64 {
self.total.as_secs_f64() * 1000.0
}
pub fn average_ms(&self) -> f64 {
self.average.as_secs_f64() * 1000.0
}
pub fn min_ms(&self) -> f64 {
self.min.as_secs_f64() * 1000.0
}
pub fn max_ms(&self) -> f64 {
self.max.as_secs_f64() * 1000.0
}
pub fn median_ms(&self) -> f64 {
self.median.as_secs_f64() * 1000.0
}
}
pub fn current_timestamp() -> String {
let now: DateTime<Utc> = Utc::now();
now.format("%Y-%m-%d %H:%M:%S%.3f UTC").to_string()
}
pub fn current_unix_timestamp() -> i64 {
Utc::now().timestamp()
}
pub fn system_time_to_datetime(time: SystemTime) -> DateTime<Utc> {
DateTime::from(time)
}
pub fn duration_from_ms(ms: u64) -> Duration {
Duration::from_millis(ms)
}
pub fn duration_from_micros(micros: u64) -> Duration {
Duration::from_micros(micros)
}
pub fn duration_from_nanos(nanos: u64) -> Duration {
Duration::from_nanos(nanos)
}