use crate::{FormatFloatValue, FormatIntegerValue, RoundFloat, Unit};
use std::time::Duration;
const SECONDS_IN_DAY: f64 = 86400.0;
const SECONDS_IN_HOUR: f64 = 3600.0;
const SECONDS_IN_MINUTE: f64 = 60.0;
const EPSILON: f64 = 1e-10;
#[derive(Debug, Default, PartialEq)]
pub struct Time {
pub days: u64,
pub hours: u8,
pub minutes: u8,
pub seconds: f64,
}
impl Time {
pub fn new(duration: Duration) -> Time {
let dur_secs: f64 = duration.as_secs_f64();
let remaining_day = dur_secs % SECONDS_IN_DAY;
let remaining_hour = remaining_day % SECONDS_IN_HOUR;
let days = (dur_secs / SECONDS_IN_DAY).floor() as u64;
let hours = (remaining_day / SECONDS_IN_HOUR).floor() as u8;
let minutes = (remaining_hour / SECONDS_IN_MINUTE).floor() as u8;
let seconds = (remaining_hour % SECONDS_IN_MINUTE).round_float(9);
Time {
days,
hours,
minutes,
seconds,
}
}
pub fn format_time(&self) -> String {
let mut parts = Vec::new();
if self.days > 0 {
parts.push(self.days.format_unit(Unit::Day));
}
if self.hours > 0 || !parts.is_empty() {
parts.push(self.hours.format_unit(Unit::Hour));
}
if self.minutes > 0 || !parts.is_empty() {
parts.push(self.minutes.format_unit(Unit::Minute));
}
let decimal: usize = self.calculate_decimal();
parts.push(self.seconds.format_float_unit(decimal, Unit::Second));
parts.join(", ")
}
fn calculate_decimal(&self) -> usize {
let sec = self.seconds;
if sec < EPSILON {
1
} else if sec >= 1.0 {
3
} else if sec >= 0.001 {
6
} else {
9
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn times_new() {
let duration = Duration::from_secs(86400 + 3600 + 60 + 1); let time = Time::new(duration);
assert_eq!(time.days, 1);
assert_eq!(time.hours, 1);
assert_eq!(time.minutes, 1);
assert_eq!(time.seconds, 1.0);
let duration = Duration::from_secs_f64(3661.5); let time = Time::new(duration);
assert_eq!(time.days, 0);
assert_eq!(time.hours, 1);
assert_eq!(time.minutes, 1);
assert_eq!(time.seconds, 1.5);
let duration = Duration::from_secs(0);
let time = Time::new(duration);
assert_eq!(time.days, 0);
assert_eq!(time.hours, 0);
assert_eq!(time.minutes, 0);
assert_eq!(time.seconds, 0.0);
let duration = Duration::from_secs_f64(SECONDS_IN_DAY * 2.5);
let time = Time::new(duration);
assert_eq!(time.days, 2);
assert_eq!(time.hours, 12);
assert_eq!(time.minutes, 0);
assert_eq!(time.seconds, 0.0);
}
#[test]
fn times_format() {
let time = Time {
days: 1,
hours: 2,
minutes: 3,
seconds: 4.567002,
};
assert_eq!(
time.format_time(),
"1 day, 2 hours, 3 minutes, 4.567 seconds"
);
let time = Time {
days: 0,
hours: 2,
minutes: 3,
seconds: 4.567,
};
assert_eq!(time.format_time(), "2 hours, 3 minutes, 4.567 seconds");
let time = Time {
days: 0,
hours: 0,
minutes: 3,
seconds: 4.567111,
};
assert_eq!(time.format_time(), "3 minutes, 4.567 seconds");
let time = Time {
days: 0,
hours: 0,
minutes: 0,
seconds: 4.567000444,
};
assert_eq!(time.format_time(), "4.567 seconds");
let time = Time {
days: 1,
hours: 0,
minutes: 0,
seconds: 0.0,
};
assert_eq!(time.format_time(), "1 day, 0 hour, 0 minute, 0.0 second");
let time = Time {
days: 1,
hours: 2,
minutes: 0,
seconds: 0.0,
};
assert_eq!(time.format_time(), "1 day, 2 hours, 0 minute, 0.0 second");
}
#[test]
fn times_default() {
let time = Time {
days: 0,
hours: 0,
minutes: 0,
seconds: 0.0,
};
let time_default = Time::default();
assert_eq!(time, time_default);
assert_eq!(time.format_time(), "0.0 second");
}
}