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 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 pub fn new(secs: u64, nanos: u32) -> Duration {
99 time::Duration::new(secs, nanos).into()
100 }
101 pub fn to_raw(&self) -> String {
103 self.to_string()
104 }
105 pub fn nanos(&self) -> u128 {
107 self.0.as_nanos()
108 }
109 pub fn micros(&self) -> u128 {
111 self.0.as_micros()
112 }
113 pub fn millis(&self) -> u128 {
115 self.0.as_millis()
116 }
117 pub fn secs(&self) -> u64 {
119 self.0.as_secs()
120 }
121 pub fn mins(&self) -> u64 {
123 self.0.as_secs() / SECONDS_PER_MINUTE
124 }
125 pub fn hours(&self) -> u64 {
127 self.0.as_secs() / SECONDS_PER_HOUR
128 }
129 pub fn days(&self) -> u64 {
131 self.0.as_secs() / SECONDS_PER_DAY
132 }
133 pub fn weeks(&self) -> u64 {
135 self.0.as_secs() / SECONDS_PER_WEEK
136 }
137 pub fn years(&self) -> u64 {
139 self.0.as_secs() / SECONDS_PER_YEAR
140 }
141 pub fn from_nanos(nanos: u64) -> Duration {
143 time::Duration::from_nanos(nanos).into()
144 }
145 pub fn from_micros(micros: u64) -> Duration {
147 time::Duration::from_micros(micros).into()
148 }
149 pub fn from_millis(millis: u64) -> Duration {
151 time::Duration::from_millis(millis).into()
152 }
153 pub fn from_secs(secs: u64) -> Duration {
155 time::Duration::from_secs(secs).into()
156 }
157 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 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 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 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 let secs = self.0.as_secs();
179 let nano = self.0.subsec_nanos();
180 if secs == 0 && nano == 0 {
182 return write!(f, "0ns");
183 }
184 let year = secs / SECONDS_PER_YEAR;
186 let secs = secs % SECONDS_PER_YEAR;
187 let week = secs / SECONDS_PER_WEEK;
189 let secs = secs % SECONDS_PER_WEEK;
190 let days = secs / SECONDS_PER_DAY;
192 let secs = secs % SECONDS_PER_DAY;
193 let hour = secs / SECONDS_PER_HOUR;
195 let secs = secs % SECONDS_PER_HOUR;
196 let mins = secs / SECONDS_PER_MINUTE;
198 let secs = secs % SECONDS_PER_MINUTE;
199 let msec = nano / NANOSECONDS_PER_MILLISECOND;
201 let nano = nano % NANOSECONDS_PER_MILLISECOND;
202 let usec = nano / NANOSECONDS_PER_MICROSECOND;
204 let nano = nano % NANOSECONDS_PER_MICROSECOND;
205 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}