pub fn timestamp(epoch: i64) -> anyhow::Result<String> {
let dt = chrono::DateTime::from_timestamp(epoch, 0)
.ok_or_else(|| anyhow::anyhow!("Epoch second {epoch} out of range"))?;
Ok(dt
.with_timezone(&chrono::Local)
.format("%Y-%m-%d %H:%M:%S")
.to_string())
}
pub fn duration_ms(ms: u64) -> String {
if ms == 0 {
"-".to_string()
} else if ms < 1000 {
format!("{}ms", ms)
} else if ms < 60_000 {
format!("{:.1}s", ms as f64 / 1000.0)
} else if ms < 3_600_000 {
let mins = ms / 60_000;
let secs = (ms % 60_000) / 1000;
format!("{}m{:02}s", mins, secs)
} else {
let hours = ms / 3_600_000;
let mins = (ms % 3_600_000) / 60_000;
format!("{}h{:02}m", hours, mins)
}
}
pub fn size_bytes(bytes: u64) -> String {
const K: u64 = 1024;
const M: u64 = 1024 * 1024;
const G: u64 = 1024 * 1024 * 1024;
if bytes >= G {
format!("{:.1}G", bytes as f64 / G as f64)
} else if bytes >= M {
format!("{:.1}M", bytes as f64 / M as f64)
} else if bytes >= K {
format!("{:.1}K", bytes as f64 / K as f64)
} else {
format!("{}B", bytes)
}
}