use crate::sql::datetime::Datetime; use crate::sql::strand::Strand; use crate::syn; use revision::revisioned; use serde::{Deserialize, Serialize}; use std::fmt; use std::iter::Sum; use std::ops; use std::ops::Deref; use std::str::FromStr; use std::time; pub(crate) static SECONDS_PER_YEAR: u64 = 365 * SECONDS_PER_DAY; pub(crate) static SECONDS_PER_WEEK: u64 = 7 * SECONDS_PER_DAY; pub(crate) static SECONDS_PER_DAY: u64 = 24 * SECONDS_PER_HOUR; pub(crate) static SECONDS_PER_HOUR: u64 = 60 * SECONDS_PER_MINUTE; pub(crate) static SECONDS_PER_MINUTE: u64 = 60; pub(crate) static NANOSECONDS_PER_MILLISECOND: u32 = 1000000; pub(crate) static NANOSECONDS_PER_MICROSECOND: u32 = 1000; pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Duration"; #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] #[serde(rename = "$surrealdb::private::sql::Duration")] #[revisioned(revision = 1)] pub struct Duration(pub time::Duration); impl From for Duration { fn from(v: time::Duration) -> Self { Self(v) } } impl From for time::Duration { fn from(s: Duration) -> Self { s.0 } } impl FromStr for Duration { type Err = (); fn from_str(s: &str) -> Result { Self::try_from(s) } } impl TryFrom for Duration { type Error = (); fn try_from(v: String) -> Result { Self::try_from(v.as_str()) } } impl TryFrom for Duration { type Error = (); fn try_from(v: Strand) -> Result { Self::try_from(v.as_str()) } } impl TryFrom<&str> for Duration { type Error = (); fn try_from(v: &str) -> Result { match syn::duration(v) { Ok(v) => Ok(v), _ => Err(()), } } } impl Deref for Duration { type Target = time::Duration; fn deref(&self) -> &Self::Target { &self.0 } } impl Duration { /// Convert the Duration to a raw String pub fn to_raw(&self) -> String { self.to_string() } /// Get the total number of nanoseconds pub fn nanos(&self) -> u128 { self.0.as_nanos() } /// Get the total number of microseconds pub fn micros(&self) -> u128 { self.0.as_micros() } /// Get the total number of milliseconds pub fn millis(&self) -> u128 { self.0.as_millis() } /// Get the total number of seconds pub fn secs(&self) -> u64 { self.0.as_secs() } /// Get the total number of minutes pub fn mins(&self) -> u64 { self.0.as_secs() / SECONDS_PER_MINUTE } /// Get the total number of hours pub fn hours(&self) -> u64 { self.0.as_secs() / SECONDS_PER_HOUR } /// Get the total number of dats pub fn days(&self) -> u64 { self.0.as_secs() / SECONDS_PER_DAY } /// Get the total number of months pub fn weeks(&self) -> u64 { self.0.as_secs() / SECONDS_PER_WEEK } /// Get the total number of years pub fn years(&self) -> u64 { self.0.as_secs() / SECONDS_PER_YEAR } /// Create a duration from nanoseconds pub fn from_nanos(nanos: u64) -> Duration { time::Duration::from_nanos(nanos).into() } /// Create a duration from microseconds pub fn from_micros(micros: u64) -> Duration { time::Duration::from_micros(micros).into() } /// Create a duration from milliseconds pub fn from_millis(millis: u64) -> Duration { time::Duration::from_millis(millis).into() } /// Create a duration from seconds pub fn from_secs(secs: u64) -> Duration { time::Duration::from_secs(secs).into() } /// Create a duration from minutes pub fn from_mins(mins: u64) -> Duration { time::Duration::from_secs(mins * SECONDS_PER_MINUTE).into() } /// Create a duration from hours pub fn from_hours(hours: u64) -> Duration { time::Duration::from_secs(hours * SECONDS_PER_HOUR).into() } /// Create a duration from days pub fn from_days(days: u64) -> Duration { time::Duration::from_secs(days * SECONDS_PER_DAY).into() } /// Create a duration from weeks pub fn from_weeks(days: u64) -> Duration { time::Duration::from_secs(days * SECONDS_PER_WEEK).into() } } impl fmt::Display for Duration { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Split up the duration let secs = self.0.as_secs(); let nano = self.0.subsec_nanos(); // Ensure no empty output if secs == 0 && nano == 0 { return write!(f, "0ns"); } // Calculate the total years let year = secs / SECONDS_PER_YEAR; let secs = secs % SECONDS_PER_YEAR; // Calculate the total weeks let week = secs / SECONDS_PER_WEEK; let secs = secs % SECONDS_PER_WEEK; // Calculate the total days let days = secs / SECONDS_PER_DAY; let secs = secs % SECONDS_PER_DAY; // Calculate the total hours let hour = secs / SECONDS_PER_HOUR; let secs = secs % SECONDS_PER_HOUR; // Calculate the total minutes let mins = secs / SECONDS_PER_MINUTE; let secs = secs % SECONDS_PER_MINUTE; // Calculate the total milliseconds let msec = nano / NANOSECONDS_PER_MILLISECOND; let nano = nano % NANOSECONDS_PER_MILLISECOND; // Calculate the total microseconds let usec = nano / NANOSECONDS_PER_MICROSECOND; let nano = nano % NANOSECONDS_PER_MICROSECOND; // Write the different parts if year > 0 { write!(f, "{year}y")?; } if week > 0 { write!(f, "{week}w")?; } if days > 0 { write!(f, "{days}d")?; } if hour > 0 { write!(f, "{hour}h")?; } if mins > 0 { write!(f, "{mins}m")?; } if secs > 0 { write!(f, "{secs}s")?; } if msec > 0 { write!(f, "{msec}ms")?; } if usec > 0 { write!(f, "{usec}µs")?; } if nano > 0 { write!(f, "{nano}ns")?; } Ok(()) } } impl ops::Add for Duration { type Output = Self; fn add(self, other: Self) -> Self { match self.0.checked_add(other.0) { Some(v) => Duration::from(v), None => Duration::from(time::Duration::MAX), } } } impl<'a, 'b> ops::Add<&'b Duration> for &'a Duration { type Output = Duration; fn add(self, other: &'b Duration) -> Duration { match self.0.checked_add(other.0) { Some(v) => Duration::from(v), None => Duration::from(time::Duration::MAX), } } } impl ops::Sub for Duration { type Output = Self; fn sub(self, other: Self) -> Self { match self.0.checked_sub(other.0) { Some(v) => Duration::from(v), None => Duration::default(), } } } impl<'a, 'b> ops::Sub<&'b Duration> for &'a Duration { type Output = Duration; fn sub(self, other: &'b Duration) -> Duration { match self.0.checked_sub(other.0) { Some(v) => Duration::from(v), None => Duration::default(), } } } impl ops::Add for Duration { type Output = Datetime; fn add(self, other: Datetime) -> Datetime { match chrono::Duration::from_std(self.0) { Ok(d) => Datetime::from(other.0 + d), Err(_) => Datetime::default(), } } } impl ops::Sub for Duration { type Output = Datetime; fn sub(self, other: Datetime) -> Datetime { match chrono::Duration::from_std(self.0) { Ok(d) => Datetime::from(other.0 - d), Err(_) => Datetime::default(), } } } impl Sum for Duration { fn sum(iter: I) -> Duration where I: Iterator, { iter.fold(Duration::default(), |a, b| a + b) } } impl<'a> Sum<&'a Self> for Duration { fn sum(iter: I) -> Duration where I: Iterator, { iter.fold(Duration::default(), |a, b| &a + b) } }