surrealdb_core/sql/
duration.rs1use 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 pub fn new(secs: u64, nanos: u32) -> Duration {
95 time::Duration::new(secs, nanos).into()
96 }
97 pub fn to_raw(&self) -> String {
99 self.to_string()
100 }
101 pub fn nanos(&self) -> u128 {
103 self.0.as_nanos()
104 }
105 pub fn micros(&self) -> u128 {
107 self.0.as_micros()
108 }
109 pub fn millis(&self) -> u128 {
111 self.0.as_millis()
112 }
113 pub fn secs(&self) -> u64 {
115 self.0.as_secs()
116 }
117 pub fn mins(&self) -> u64 {
119 self.0.as_secs() / SECONDS_PER_MINUTE
120 }
121 pub fn hours(&self) -> u64 {
123 self.0.as_secs() / SECONDS_PER_HOUR
124 }
125 pub fn days(&self) -> u64 {
127 self.0.as_secs() / SECONDS_PER_DAY
128 }
129 pub fn weeks(&self) -> u64 {
131 self.0.as_secs() / SECONDS_PER_WEEK
132 }
133 pub fn years(&self) -> u64 {
135 self.0.as_secs() / SECONDS_PER_YEAR
136 }
137 pub fn from_nanos(nanos: u64) -> Duration {
139 time::Duration::from_nanos(nanos).into()
140 }
141 pub fn from_micros(micros: u64) -> Duration {
143 time::Duration::from_micros(micros).into()
144 }
145 pub fn from_millis(millis: u64) -> Duration {
147 time::Duration::from_millis(millis).into()
148 }
149 pub fn from_secs(secs: u64) -> Duration {
151 time::Duration::from_secs(secs).into()
152 }
153 pub fn from_mins(mins: u64) -> Duration {
155 time::Duration::from_secs(mins * SECONDS_PER_MINUTE).into()
156 }
157 pub fn from_hours(hours: u64) -> Duration {
159 time::Duration::from_secs(hours * SECONDS_PER_HOUR).into()
160 }
161 pub fn from_days(days: u64) -> Duration {
163 time::Duration::from_secs(days * SECONDS_PER_DAY).into()
164 }
165 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 let secs = self.0.as_secs();
175 let nano = self.0.subsec_nanos();
176 if secs == 0 && nano == 0 {
178 return write!(f, "0ns");
179 }
180 let year = secs / SECONDS_PER_YEAR;
182 let secs = secs % SECONDS_PER_YEAR;
183 let week = secs / SECONDS_PER_WEEK;
185 let secs = secs % SECONDS_PER_WEEK;
186 let days = secs / SECONDS_PER_DAY;
188 let secs = secs % SECONDS_PER_DAY;
189 let hour = secs / SECONDS_PER_HOUR;
191 let secs = secs % SECONDS_PER_HOUR;
192 let mins = secs / SECONDS_PER_MINUTE;
194 let secs = secs % SECONDS_PER_MINUTE;
195 let msec = nano / NANOSECONDS_PER_MILLISECOND;
197 let nano = nano % NANOSECONDS_PER_MILLISECOND;
198 let usec = nano / NANOSECONDS_PER_MICROSECOND;
200 let nano = nano % NANOSECONDS_PER_MICROSECOND;
201 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}