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