1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
use std::ops::{AddAssign, SubAssign}; use std::str::FromStr; use std::time::Duration as StdDuration; use std::num::ParseFloatError; use chrono::Duration; #[derive(From, Add, Sub, Neg, Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct TimeSpan(Duration); impl TimeSpan { pub fn zero() -> Self { Default::default() } pub fn from_milliseconds(milliseconds: f64) -> Self { TimeSpan(Duration::microseconds((milliseconds * 1_000.0) as i64)) } pub fn from_seconds(seconds: f64) -> Self { TimeSpan(Duration::microseconds((seconds * 1_000_000.0) as i64)) } pub fn from_days(days: f64) -> Self { TimeSpan(Duration::microseconds( (days * 24.0 * 3600.0 * 1_000_000.0) as i64, )) } pub fn option_op<F, R>(a: Option<TimeSpan>, b: Option<TimeSpan>, f: F) -> Option<R> where F: FnOnce(TimeSpan, TimeSpan) -> R, { match (a, b) { (Some(a), Some(b)) => Some(f(a, b)), _ => None, } } pub fn option_add(a: Option<TimeSpan>, b: Option<TimeSpan>) -> Option<TimeSpan> { TimeSpan::option_op(a, b, |a, b| a + b) } pub fn option_sub(a: Option<TimeSpan>, b: Option<TimeSpan>) -> Option<TimeSpan> { TimeSpan::option_op(a, b, |a, b| a - b) } pub fn to_duration(&self) -> Duration { self.0 } pub fn total_seconds(&self) -> f64 { self.0.num_microseconds().unwrap() as f64 / 1_000_000.0 } pub fn total_milliseconds(&self) -> f64 { self.0.num_microseconds().unwrap() as f64 / 1_000.0 } pub fn parse_opt<S>(text: S) -> Result<Option<TimeSpan>, ParseError> where S: AsRef<str>, { let text = text.as_ref(); if text.trim().is_empty() { Ok(None) } else { Ok(Some(text.parse()?)) } } } quick_error! { #[derive(Debug)] pub enum ParseError { Float(err: ParseFloatError) { from() } } } impl FromStr for TimeSpan { type Err = ParseError; fn from_str(mut text: &str) -> Result<Self, ParseError> { let factor = if text.starts_with('-') { text = &text[1..]; -1.0 } else if text.starts_with('−') { text = &text[3..]; -1.0 } else { 1.0 }; let mut seconds = 0.0; for split in text.split(':') { seconds = 60.0 * seconds + split.parse::<f64>()?; } Ok(TimeSpan::from_seconds(factor * seconds)) } } impl Default for TimeSpan { fn default() -> Self { TimeSpan(Duration::nanoseconds(0)) } } impl From<StdDuration> for TimeSpan { fn from(duration: StdDuration) -> Self { TimeSpan(Duration::from_std(duration).unwrap()) } } impl AddAssign for TimeSpan { fn add_assign(&mut self, rhs: TimeSpan) { self.0 = self.0 + rhs.0; } } impl SubAssign for TimeSpan { fn sub_assign(&mut self, rhs: TimeSpan) { self.0 = self.0 - rhs.0; } }