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