surrealdb_core/sql/
duration.rs

1use crate::err::Error;
2use crate::sql::datetime::Datetime;
3use crate::sql::statements::info::InfoStructure;
4use crate::sql::strand::Strand;
5use crate::sql::Value;
6use crate::syn;
7use revision::revisioned;
8use serde::{Deserialize, Serialize};
9use std::fmt;
10use std::iter::Sum;
11use std::ops;
12use std::ops::Deref;
13use std::str::FromStr;
14use std::time;
15
16use super::value::{TryAdd, TrySub};
17
18pub(crate) static SECONDS_PER_YEAR: u64 = 365 * SECONDS_PER_DAY;
19pub(crate) static SECONDS_PER_WEEK: u64 = 7 * SECONDS_PER_DAY;
20pub(crate) static SECONDS_PER_DAY: u64 = 24 * SECONDS_PER_HOUR;
21pub(crate) static SECONDS_PER_HOUR: u64 = 60 * SECONDS_PER_MINUTE;
22pub(crate) static SECONDS_PER_MINUTE: u64 = 60;
23pub(crate) static NANOSECONDS_PER_MILLISECOND: u32 = 1000000;
24pub(crate) static NANOSECONDS_PER_MICROSECOND: u32 = 1000;
25
26pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Duration";
27
28#[revisioned(revision = 1)]
29#[derive(
30	Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash, Ord,
31)]
32#[serde(rename = "$surrealdb::private::sql::Duration")]
33#[non_exhaustive]
34pub struct Duration(pub time::Duration);
35
36impl From<time::Duration> for Duration {
37	fn from(v: time::Duration) -> Self {
38		Self(v)
39	}
40}
41
42impl From<Duration> for time::Duration {
43	fn from(s: Duration) -> Self {
44		s.0
45	}
46}
47
48impl From<time::Duration> for Value {
49	fn from(value: time::Duration) -> Self {
50		Self::Duration(value.into())
51	}
52}
53
54impl FromStr for Duration {
55	type Err = ();
56	fn from_str(s: &str) -> Result<Self, Self::Err> {
57		Self::try_from(s)
58	}
59}
60
61impl TryFrom<String> for Duration {
62	type Error = ();
63	fn try_from(v: String) -> Result<Self, Self::Error> {
64		Self::try_from(v.as_str())
65	}
66}
67
68impl TryFrom<Strand> for Duration {
69	type Error = ();
70	fn try_from(v: Strand) -> Result<Self, Self::Error> {
71		Self::try_from(v.as_str())
72	}
73}
74
75impl TryFrom<&str> for Duration {
76	type Error = ();
77	fn try_from(v: &str) -> Result<Self, Self::Error> {
78		match syn::duration(v) {
79			Ok(v) => Ok(v),
80			_ => Err(()),
81		}
82	}
83}
84
85impl Deref for Duration {
86	type Target = time::Duration;
87	fn deref(&self) -> &Self::Target {
88		&self.0
89	}
90}
91
92impl Duration {
93	/// Create a duration from both seconds and nanoseconds components
94	pub fn new(secs: u64, nanos: u32) -> Duration {
95		time::Duration::new(secs, nanos).into()
96	}
97	/// Convert the Duration to a raw String
98	pub fn to_raw(&self) -> String {
99		self.to_string()
100	}
101	/// Get the total number of nanoseconds
102	pub fn nanos(&self) -> u128 {
103		self.0.as_nanos()
104	}
105	/// Get the total number of microseconds
106	pub fn micros(&self) -> u128 {
107		self.0.as_micros()
108	}
109	/// Get the total number of milliseconds
110	pub fn millis(&self) -> u128 {
111		self.0.as_millis()
112	}
113	/// Get the total number of seconds
114	pub fn secs(&self) -> u64 {
115		self.0.as_secs()
116	}
117	/// Get the total number of minutes
118	pub fn mins(&self) -> u64 {
119		self.0.as_secs() / SECONDS_PER_MINUTE
120	}
121	/// Get the total number of hours
122	pub fn hours(&self) -> u64 {
123		self.0.as_secs() / SECONDS_PER_HOUR
124	}
125	/// Get the total number of dats
126	pub fn days(&self) -> u64 {
127		self.0.as_secs() / SECONDS_PER_DAY
128	}
129	/// Get the total number of months
130	pub fn weeks(&self) -> u64 {
131		self.0.as_secs() / SECONDS_PER_WEEK
132	}
133	/// Get the total number of years
134	pub fn years(&self) -> u64 {
135		self.0.as_secs() / SECONDS_PER_YEAR
136	}
137	/// Create a duration from nanoseconds
138	pub fn from_nanos(nanos: u64) -> Duration {
139		time::Duration::from_nanos(nanos).into()
140	}
141	/// Create a duration from microseconds
142	pub fn from_micros(micros: u64) -> Duration {
143		time::Duration::from_micros(micros).into()
144	}
145	/// Create a duration from milliseconds
146	pub fn from_millis(millis: u64) -> Duration {
147		time::Duration::from_millis(millis).into()
148	}
149	/// Create a duration from seconds
150	pub fn from_secs(secs: u64) -> Duration {
151		time::Duration::from_secs(secs).into()
152	}
153	/// Create a duration from minutes
154	pub fn from_mins(mins: u64) -> Duration {
155		time::Duration::from_secs(mins * SECONDS_PER_MINUTE).into()
156	}
157	/// Create a duration from hours
158	pub fn from_hours(hours: u64) -> Duration {
159		time::Duration::from_secs(hours * SECONDS_PER_HOUR).into()
160	}
161	/// Create a duration from days
162	pub fn from_days(days: u64) -> Duration {
163		time::Duration::from_secs(days * SECONDS_PER_DAY).into()
164	}
165	/// Create a duration from weeks
166	pub fn from_weeks(days: u64) -> Duration {
167		time::Duration::from_secs(days * SECONDS_PER_WEEK).into()
168	}
169}
170
171impl fmt::Display for Duration {
172	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
173		// Split up the duration
174		let secs = self.0.as_secs();
175		let nano = self.0.subsec_nanos();
176		// Ensure no empty output
177		if secs == 0 && nano == 0 {
178			return write!(f, "0ns");
179		}
180		// Calculate the total years
181		let year = secs / SECONDS_PER_YEAR;
182		let secs = secs % SECONDS_PER_YEAR;
183		// Calculate the total weeks
184		let week = secs / SECONDS_PER_WEEK;
185		let secs = secs % SECONDS_PER_WEEK;
186		// Calculate the total days
187		let days = secs / SECONDS_PER_DAY;
188		let secs = secs % SECONDS_PER_DAY;
189		// Calculate the total hours
190		let hour = secs / SECONDS_PER_HOUR;
191		let secs = secs % SECONDS_PER_HOUR;
192		// Calculate the total minutes
193		let mins = secs / SECONDS_PER_MINUTE;
194		let secs = secs % SECONDS_PER_MINUTE;
195		// Calculate the total milliseconds
196		let msec = nano / NANOSECONDS_PER_MILLISECOND;
197		let nano = nano % NANOSECONDS_PER_MILLISECOND;
198		// Calculate the total microseconds
199		let usec = nano / NANOSECONDS_PER_MICROSECOND;
200		let nano = nano % NANOSECONDS_PER_MICROSECOND;
201		// Write the different parts
202		if year > 0 {
203			write!(f, "{year}y")?;
204		}
205		if week > 0 {
206			write!(f, "{week}w")?;
207		}
208		if days > 0 {
209			write!(f, "{days}d")?;
210		}
211		if hour > 0 {
212			write!(f, "{hour}h")?;
213		}
214		if mins > 0 {
215			write!(f, "{mins}m")?;
216		}
217		if secs > 0 {
218			write!(f, "{secs}s")?;
219		}
220		if msec > 0 {
221			write!(f, "{msec}ms")?;
222		}
223		if usec > 0 {
224			write!(f, "{usec}µs")?;
225		}
226		if nano > 0 {
227			write!(f, "{nano}ns")?;
228		}
229		Ok(())
230	}
231}
232
233impl ops::Add for Duration {
234	type Output = Self;
235	fn add(self, other: Self) -> Self {
236		match self.0.checked_add(other.0) {
237			Some(v) => Duration::from(v),
238			None => Duration::from(time::Duration::MAX),
239		}
240	}
241}
242
243impl TryAdd for Duration {
244	type Output = Self;
245	fn try_add(self, other: Self) -> Result<Self, Error> {
246		self.0
247			.checked_add(other.0)
248			.ok_or_else(|| Error::ArithmeticOverflow(format!("{self} + {other}")))
249			.map(Duration::from)
250	}
251}
252
253impl<'b> ops::Add<&'b Duration> for &Duration {
254	type Output = Duration;
255	fn add(self, other: &'b Duration) -> Duration {
256		match self.0.checked_add(other.0) {
257			Some(v) => Duration::from(v),
258			None => Duration::from(time::Duration::MAX),
259		}
260	}
261}
262
263impl<'b> TryAdd<&'b Duration> for &Duration {
264	type Output = Duration;
265	fn try_add(self, other: &'b Duration) -> Result<Duration, Error> {
266		self.0
267			.checked_add(other.0)
268			.ok_or_else(|| Error::ArithmeticOverflow(format!("{self} + {other}")))
269			.map(Duration::from)
270	}
271}
272
273impl ops::Sub for Duration {
274	type Output = Self;
275	fn sub(self, other: Self) -> Self {
276		match self.0.checked_sub(other.0) {
277			Some(v) => Duration::from(v),
278			None => Duration::default(),
279		}
280	}
281}
282
283impl TrySub for Duration {
284	type Output = Self;
285	fn try_sub(self, other: Self) -> Result<Self, Error> {
286		self.0
287			.checked_sub(other.0)
288			.ok_or_else(|| Error::ArithmeticNegativeOverflow(format!("{self} - {other}")))
289			.map(Duration::from)
290	}
291}
292
293impl<'b> ops::Sub<&'b Duration> for &Duration {
294	type Output = Duration;
295	fn sub(self, other: &'b Duration) -> Duration {
296		match self.0.checked_sub(other.0) {
297			Some(v) => Duration::from(v),
298			None => Duration::default(),
299		}
300	}
301}
302
303impl<'b> TrySub<&'b Duration> for &Duration {
304	type Output = Duration;
305	fn try_sub(self, other: &'b Duration) -> Result<Duration, Error> {
306		self.0
307			.checked_sub(other.0)
308			.ok_or_else(|| Error::ArithmeticNegativeOverflow(format!("{self} - {other}")))
309			.map(Duration::from)
310	}
311}
312
313impl ops::Add<Datetime> for Duration {
314	type Output = Datetime;
315	fn add(self, other: Datetime) -> Datetime {
316		match chrono::Duration::from_std(self.0) {
317			Ok(d) => match other.0.checked_add_signed(d) {
318				Some(v) => Datetime::from(v),
319				None => Datetime::default(),
320			},
321			Err(_) => Datetime::default(),
322		}
323	}
324}
325
326impl TryAdd<Datetime> for Duration {
327	type Output = Datetime;
328	fn try_add(self, other: Datetime) -> Result<Datetime, Error> {
329		match chrono::Duration::from_std(self.0) {
330			Ok(d) => match other.0.checked_add_signed(d) {
331				Some(v) => Ok(Datetime::from(v)),
332				None => Err(Error::ArithmeticOverflow(format!("{self} + {other}"))),
333			},
334			Err(_) => Err(Error::ArithmeticOverflow(format!("{self} + {other}"))),
335		}
336	}
337}
338
339impl ops::Sub<Datetime> for Duration {
340	type Output = Datetime;
341	fn sub(self, other: Datetime) -> Datetime {
342		match chrono::Duration::from_std(self.0) {
343			Ok(d) => match other.0.checked_sub_signed(d) {
344				Some(v) => Datetime::from(v),
345				None => Datetime::default(),
346			},
347			Err(_) => Datetime::default(),
348		}
349	}
350}
351
352impl TrySub<Datetime> for Duration {
353	type Output = Datetime;
354	fn try_sub(self, other: Datetime) -> Result<Datetime, Error> {
355		match chrono::Duration::from_std(self.0) {
356			Ok(d) => match other.0.checked_sub_signed(d) {
357				Some(v) => Ok(Datetime::from(v)),
358				None => Err(Error::ArithmeticNegativeOverflow(format!("{self} - {other}"))),
359			},
360			Err(_) => Err(Error::ArithmeticNegativeOverflow(format!("{self} - {other}"))),
361		}
362	}
363}
364
365impl Sum<Self> for Duration {
366	fn sum<I>(iter: I) -> Duration
367	where
368		I: Iterator<Item = Self>,
369	{
370		iter.fold(Duration::default(), |a, b| a + b)
371	}
372}
373
374impl<'a> Sum<&'a Self> for Duration {
375	fn sum<I>(iter: I) -> Duration
376	where
377		I: Iterator<Item = &'a Self>,
378	{
379		iter.fold(Duration::default(), |a, b| &a + b)
380	}
381}
382
383impl InfoStructure for Duration {
384	fn structure(self) -> Value {
385		self.to_string().into()
386	}
387}