pub use parse_duration::parse::Error as ParseError;
use crate::math::AsPrimitive;
use crate::prelude::*;
use parse_duration::parse;
#[derive(Clone, Copy, Default, PartialEq, PartialOrd)]
pub struct Duration {
secs: f64,
}
impl Duration {
pub const INFINITE: Self = Self { secs: f64::INFINITY };
pub const ZERO: Self = Self { secs: 0.0 };
pub fn weeks(weeks: impl AsPrimitive<f64>) -> Self {
Self::secs(weeks.as_() * 7.0 * 24.0 * 60.0 * 60.0)
}
pub fn days(days: impl AsPrimitive<f64>) -> Self {
Self::secs(days.as_() * 24.0 * 60.0 * 60.0)
}
pub fn hours(hours: impl AsPrimitive<f64>) -> Self {
Self::secs(hours.as_() * 60.0 * 60.0)
}
pub fn mins(mins: impl AsPrimitive<f64>) -> Self {
Self::secs(mins.as_() * 60.0)
}
pub fn secs(secs: impl AsPrimitive<f64>) -> Self {
let secs = secs.as_();
assert!(!secs.is_nan(), "Duration cannot be NaN.");
Self { secs: secs.max(0.0) }
}
pub fn hz(hz: impl AsPrimitive<f64>) -> Self {
Self::secs(1.0 / hz.as_())
}
pub fn ms(ms: impl AsPrimitive<f64>) -> Duration {
Self::secs(ms.as_() / 1000.0)
}
pub fn as_weeks(self) -> f64 {
self.as_secs() / 7.0 / 24.0 / 60.0 / 60.0
}
pub fn as_days(self) -> f64 {
self.as_secs() / 24.0 / 60.0 / 60.0
}
pub fn as_hours(self) -> f64 {
self.as_secs() / 60.0 / 60.0
}
pub fn as_mins(self) -> f64 {
self.as_secs() / 60.0
}
pub const fn as_secs(self) -> f64 {
self.secs
}
pub fn as_hz(self) -> f64 {
1.0 / self.secs
}
pub fn as_ms(self) -> f64 {
self.secs * 1000.0
}
pub fn is_finite(&self) -> bool {
self.secs.is_finite()
}
pub fn is_infinite(&self) -> bool {
self.secs.is_infinite()
}
pub fn to_std(self) -> std::time::Duration {
const MAX_SAFE_INT: f64 = 9007199254740991f64;
std::time::Duration::from_secs_f64(self.secs.min(MAX_SAFE_INT))
}
}
impl Add<Self> for Duration {
type Output = Self;
fn add(mut self, rhs: Self) -> Self::Output {
self += rhs;
self
}
}
impl AddAssign<Self> for Duration {
fn add_assign(&mut self, rhs: Self) {
self.secs += rhs.secs;
}
}
impl Debug for Duration {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\"{}\"", self)
}
}
impl Display for Duration {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.secs.is_infinite() {
write!(f, "forever")
} else if self.secs < 2.0 {
write!(f, "{} ms", self.as_ms().round_to_places(3))
} else if self.secs < 120.0 {
write!(f, "{} secs", self.as_secs().round_to_places(3))
} else if self.secs < 7_200.0 {
write!(f, "{} mins", self.as_mins().round_to_places(2))
} else if self.secs < 172_800.0 {
write!(f, "{} hours", self.as_hours().round_to_places(2))
} else if self.secs < 604_800.0 {
write!(f, "{} days", self.as_days().round_to_places(2))
} else if self.secs < 31_557_600.0 {
write!(f, "{} weeks", self.as_weeks().round_to_places(1))
} else {
write!(f, "{} years", (self.secs / 31_557_600.0).round_to_places(1))
}
}
}
impl<T> Div<T> for Duration
where
T: AsPrimitive<f64>,
{
type Output = Self;
fn div(mut self, rhs: T) -> Self::Output {
self /= rhs;
self
}
}
impl<T> DivAssign<T> for Duration
where
T: AsPrimitive<f64>,
{
fn div_assign(&mut self, rhs: T) {
self.secs = f64::max(self.secs / rhs.as_(), 0.0);
}
}
impl Eq for Duration {}
impl From<std::time::Duration> for Duration {
fn from(value: std::time::Duration) -> Self {
Self::secs(value.as_secs_f64())
}
}
impl From<chrono::Duration> for Duration {
fn from(value: chrono::Duration) -> Self {
value.to_std().unwrap_or_default().into()
}
}
impl From<Duration> for std::time::Duration {
fn from(value: Duration) -> Self {
value.to_std()
}
}
impl From<Duration> for chrono::Duration {
fn from(value: Duration) -> Self {
Self::from_std(value.to_std())
.expect("Failed to convert std::time::Duration to chrono::Duration")
}
}
impl FromStr for Duration {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse(s).map(From::from)
}
}
impl<T> Mul<T> for Duration
where
T: AsPrimitive<f64>,
{
type Output = Self;
fn mul(mut self, rhs: T) -> Self::Output {
self *= rhs;
self
}
}
impl<T> MulAssign<T> for Duration
where
T: AsPrimitive<f64>,
{
fn mul_assign(&mut self, rhs: T) {
self.secs = f64::max(self.secs * rhs.as_(), 0.0);
}
}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl Ord for Duration {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.secs.partial_cmp(&other.secs).unwrap()
}
}
impl Sub<Self> for Duration {
type Output = Self;
fn sub(mut self, rhs: Self) -> Self::Output {
self -= rhs;
self
}
}
impl SubAssign<Self> for Duration {
fn sub_assign(&mut self, rhs: Self) {
self.secs = f64::max(self.secs - rhs.secs, 0.0);
}
}