use crate::{humanise_list, plural_suffix};
use std::time::Duration;
const SECOND: u128 = 1000;
const MINUTE: u128 = SECOND * 60;
const HOUR: u128 = MINUTE * 60;
const DAY: u128 = HOUR * 24;
pub fn humanise_duration_ms(milliseconds: u128, verbose: bool) -> String {
if milliseconds == 0 {
return if verbose {
"0 seconds".to_string()
} else {
"0 secs".to_string()
};
}
let days_mod = milliseconds % DAY;
let days = (milliseconds - days_mod) / DAY;
let remaining_millis = days_mod;
let hours_mod = remaining_millis % HOUR;
let hours = (remaining_millis - hours_mod) / HOUR;
let remaining_millis = hours_mod;
let minutes_mod = remaining_millis % MINUTE;
let minutes = (remaining_millis - minutes_mod) / MINUTE;
let remaining_millis = minutes_mod;
let seconds_mod = remaining_millis % SECOND;
let seconds = (remaining_millis - seconds_mod) / SECOND;
let milliseconds = seconds_mod;
let mut vec = vec![];
if days > 0 {
vec.push(format!("{} {}", days, plural_suffix(days, "day", false)));
}
if hours > 0 {
vec.push(format!("{} {}", hours, plural_suffix(hours, "hour", false)));
}
if minutes > 0 {
vec.push(format!(
"{} {}",
minutes,
plural_suffix(minutes, if verbose { "minute" } else { "min " }, false)
));
}
if seconds > 0 {
vec.push(format!(
"{} {}",
seconds,
plural_suffix(seconds, if verbose { "second" } else { "sec" }, false)
));
}
if milliseconds > 0 {
vec.push(format!(
"{} {}",
milliseconds,
if verbose {
plural_suffix(milliseconds, "millisecond", false)
} else {
"ms".to_string()
}
));
}
humanise_list(&vec)
}
pub fn humanise_duration(duration: Duration, verbose: bool) -> String {
humanise_duration_ms(duration.as_millis(), verbose)
}
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
#[cfg(feature = "chrono")]
pub fn humanise_duration_chrono(duration: chrono::Duration, verbose: bool) -> String {
humanise_duration_ms(duration.num_milliseconds().abs() as u128, verbose)
}
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
#[cfg(feature = "time")]
pub fn humanise_duration_time(duration: time::Duration, verbose: bool) -> String {
humanise_duration_ms(duration.whole_milliseconds().abs() as u128, verbose)
}
#[cfg(test)]
mod tests {
use super::*;
const fn duration(millis: u128) -> Duration {
Duration::from_millis(millis as u64)
}
#[test]
fn single_units_verbose() {
macro_rules! test {
($duration:expr => $noun:literal, $max_safe:literal) => {
assert_eq!(
humanise_duration(duration($duration * 1), true),
concat!("1 ", $noun)
);
assert_eq!(
humanise_duration(duration($duration * 2), true),
concat!("2 ", $noun, "s")
);
assert_eq!(
humanise_duration(duration($duration * $max_safe), true),
concat!($max_safe, " ", $noun, "s")
);
};
}
test!(DAY => "day", 128);
test!(HOUR => "hour", 23);
test!(MINUTE => "minute", 59);
test!(SECOND => "second", 59);
test!(1 => "millisecond", 999);
}
#[test]
fn multiple_units_verbose() {
assert_eq!(
humanise_duration(duration(DAY + HOUR), true),
"1 day and 1 hour"
);
assert_eq!(
humanise_duration(duration(DAY + MINUTE), true),
"1 day and 1 minute"
);
assert_eq!(
humanise_duration(duration(3 * DAY + 7 * HOUR), true),
"3 days and 7 hours"
);
}
}