#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct TimeDelta {
days: i64,
nanos: i64, }
const NANOS_PER_DAY: i64 = 86_400_000_000_000;
impl TimeDelta {
#[inline]
fn new_normalize(days: i64, nanos: i64) -> Self {
let mut total_days = days;
let mut total_nanos = nanos;
if total_nanos < 0 {
let overflow_days = (total_nanos.abs() + NANOS_PER_DAY - 1) / NANOS_PER_DAY;
total_days -= overflow_days;
total_nanos += overflow_days * NANOS_PER_DAY;
}
if total_nanos >= NANOS_PER_DAY {
let overflow_days = total_nanos / NANOS_PER_DAY;
total_days += overflow_days;
total_nanos %= NANOS_PER_DAY;
}
Self {
days: total_days,
nanos: total_nanos,
}
}
#[inline]
pub fn days(days: i64) -> Self {
Self { days, nanos: 0 }
}
#[inline]
pub fn hours(hours: i64) -> Self {
Self::new_normalize(0, hours * 3_600_000_000_000)
}
#[inline]
pub fn minutes(minutes: i64) -> Self {
Self::new_normalize(0, minutes * 60_000_000_000)
}
#[inline]
pub fn seconds(seconds: i64) -> Self {
Self::new_normalize(0, seconds * 1_000_000_000)
}
#[inline]
pub fn milliseconds(millis: i64) -> Self {
Self::new_normalize(0, millis * 1_000_000)
}
#[inline]
pub fn microseconds(micros: i64) -> Self {
Self::new_normalize(0, micros * 1_000)
}
#[inline]
pub fn nanoseconds(nanos: i64) -> Self {
Self::new_normalize(0, nanos)
}
#[inline]
pub fn total_seconds(&self) -> i64 {
self.days * 86_400 + self.nanos / 1_000_000_000
}
#[inline]
pub fn total_milliseconds(&self) -> i64 {
self.days
.saturating_mul(86_400_000)
.saturating_add(self.nanos / 1_000_000)
}
#[inline]
pub fn total_nanoseconds(&self) -> i128 {
(self.days as i128) * (NANOS_PER_DAY as i128) + (self.nanos as i128)
}
#[inline]
pub fn add(&self, other: &TimeDelta) -> TimeDelta {
Self::new_normalize(self.days + other.days, self.nanos + other.nanos)
}
#[inline]
pub fn sub(&self, other: &TimeDelta) -> TimeDelta {
Self::new_normalize(self.days - other.days, self.nanos - other.nanos)
}
#[inline]
pub fn neg(&self) -> TimeDelta {
if self.nanos == 0 {
Self {
days: -self.days,
nanos: 0,
}
} else {
Self {
days: -self.days - 1,
nanos: NANOS_PER_DAY - self.nanos,
}
}
}
#[inline]
pub fn abs(&self) -> TimeDelta {
if self.days < 0 {
self.neg()
} else {
*self
}
}
#[inline]
pub fn is_zero(&self) -> bool {
self.days == 0 && self.nanos == 0
}
}
impl std::ops::Add for TimeDelta {
type Output = TimeDelta;
fn add(self, other: TimeDelta) -> TimeDelta {
TimeDelta::add(&self, &other)
}
}
impl std::ops::Sub for TimeDelta {
type Output = TimeDelta;
fn sub(self, other: TimeDelta) -> TimeDelta {
TimeDelta::sub(&self, &other)
}
}
impl std::ops::Neg for TimeDelta {
type Output = TimeDelta;
fn neg(self) -> TimeDelta {
TimeDelta::neg(&self)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_delta_days() {
let d = TimeDelta::days(5);
assert_eq!(d.total_seconds(), 5 * 86_400);
}
#[test]
fn test_delta_hours() {
let d = TimeDelta::hours(24);
assert_eq!(d.total_seconds(), 86_400);
assert_eq!(d.days, 1);
}
#[test]
fn test_delta_minutes() {
let d = TimeDelta::minutes(120);
assert_eq!(d.total_seconds(), 7_200);
}
#[test]
fn test_delta_seconds() {
let d = TimeDelta::seconds(3600);
assert_eq!(d.total_seconds(), 3600);
}
#[test]
fn test_delta_milliseconds() {
let d = TimeDelta::milliseconds(1500);
assert_eq!(d.total_milliseconds(), 1500);
}
#[test]
fn test_delta_add() {
let d1 = TimeDelta::hours(12);
let d2 = TimeDelta::hours(13);
let sum = d1.add(&d2);
assert_eq!(sum.total_seconds(), 25 * 3600);
}
#[test]
fn test_delta_sub() {
let d1 = TimeDelta::days(5);
let d2 = TimeDelta::days(2);
let diff = d1.sub(&d2);
assert_eq!(diff.total_seconds(), 3 * 86_400);
}
#[test]
fn test_delta_neg() {
let d = TimeDelta::hours(5);
let neg_d = d.neg();
assert_eq!(neg_d.total_seconds(), -5 * 3600);
}
#[test]
fn test_delta_normalization() {
let d = TimeDelta::hours(25);
assert_eq!(d.days, 1);
assert_eq!(d.total_seconds(), 25 * 3600);
}
#[test]
fn test_delta_operators() {
let d1 = TimeDelta::days(3);
let d2 = TimeDelta::days(2);
assert_eq!((d1 + d2).total_seconds(), 5 * 86_400);
assert_eq!((d1 - d2).total_seconds(), 86_400);
assert_eq!((-d1).total_seconds(), -3 * 86_400);
}
#[test]
fn test_delta_zero() {
let d = TimeDelta::days(0);
assert!(d.is_zero());
let d = TimeDelta::seconds(1);
assert!(!d.is_zero());
}
}