Skip to main content

common_time/
duration.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::cmp::Ordering;
16use std::fmt::{Display, Formatter};
17use std::hash::{Hash, Hasher};
18
19use serde::{Deserialize, Serialize};
20
21use crate::timestamp::TimeUnit;
22
23/// [Duration] represents the elapsed time in either seconds, milliseconds, microseconds or nanoseconds.
24#[derive(Debug, Clone, Default, Copy, Serialize, Deserialize)]
25pub struct Duration {
26    value: i64,
27    unit: TimeUnit,
28}
29
30impl Duration {
31    /// Create a new Duration with value and TimeUnit.
32    pub fn new(value: i64, unit: TimeUnit) -> Self {
33        Self { value, unit }
34    }
35
36    /// Create a new Duration in second.
37    pub fn new_second(value: i64) -> Self {
38        Self {
39            value,
40            unit: TimeUnit::Second,
41        }
42    }
43
44    /// Create a new Duration in millisecond.
45    pub fn new_millisecond(value: i64) -> Self {
46        Self {
47            value,
48            unit: TimeUnit::Millisecond,
49        }
50    }
51
52    /// Create a new Duration in microsecond.
53    pub fn new_microsecond(value: i64) -> Self {
54        Self {
55            value,
56            unit: TimeUnit::Microsecond,
57        }
58    }
59
60    /// Create a new Duration in nanosecond.
61    pub fn new_nanosecond(value: i64) -> Self {
62        Self {
63            value,
64            unit: TimeUnit::Nanosecond,
65        }
66    }
67
68    /// Return the TimeUnit of current Duration.
69    pub fn unit(&self) -> TimeUnit {
70        self.unit
71    }
72
73    /// Return the value of current Duration.
74    pub fn value(&self) -> i64 {
75        self.value
76    }
77
78    /// Split a [Duration] into seconds part and nanoseconds part.
79    /// Notice the seconds part of split result is always rounded down to floor.
80    fn split(&self) -> (i64, u32) {
81        let sec_mul = (TimeUnit::Second.factor() / self.unit.factor()) as i64;
82        let nsec_mul = (self.unit.factor() / TimeUnit::Nanosecond.factor()) as i64;
83
84        let sec_div = self.value.div_euclid(sec_mul);
85        let sec_mod = self.value.rem_euclid(sec_mul);
86        // safety:  the max possible value of `sec_mod` is 999,999,999
87        let nsec = u32::try_from(sec_mod * nsec_mul).unwrap();
88        (sec_div, nsec)
89    }
90
91    /// Convert to std::time::Duration.
92    pub fn to_std_duration(self) -> std::time::Duration {
93        self.into()
94    }
95
96    pub fn negative(mut self) -> Self {
97        self.value = -self.value;
98        self
99    }
100
101    pub fn checked_negative(mut self) -> Option<Self> {
102        self.value = self.value.checked_neg()?;
103        Some(self)
104    }
105}
106
107/// Convert i64 to Duration Type.
108/// Default TimeUnit is Millisecond.
109impl From<i64> for Duration {
110    fn from(v: i64) -> Self {
111        Self {
112            value: v,
113            unit: TimeUnit::Millisecond,
114        }
115    }
116}
117
118/// return i64 value of Duration.
119impl From<Duration> for i64 {
120    fn from(d: Duration) -> Self {
121        d.value
122    }
123}
124
125/// Convert from std::time::Duration to common_time::Duration Type.
126/// The range of std::time::Duration is [0, u64::MAX seconds + 999_999_999 nanoseconds]
127/// The range of common_time::Duration is [i64::MIN, i64::MAX] with TimeUnit.
128/// If the value of std::time::Duration is out of range of common_time::Duration,
129/// it will be rounded to the nearest value.
130impl From<std::time::Duration> for Duration {
131    fn from(d: std::time::Duration) -> Self {
132        // convert as high-precision as possible
133        let value = d.as_nanos();
134        if value <= i64::MAX as u128 {
135            return Self {
136                value: value as i64,
137                unit: TimeUnit::Nanosecond,
138            };
139        }
140
141        let value = d.as_micros();
142        if value <= i64::MAX as u128 {
143            return Self {
144                value: value as i64,
145                unit: TimeUnit::Microsecond,
146            };
147        }
148
149        let value = d.as_millis();
150        if value <= i64::MAX as u128 {
151            return Self {
152                value: value as i64,
153                unit: TimeUnit::Millisecond,
154            };
155        }
156
157        let value = d.as_secs();
158        if value <= i64::MAX as u64 {
159            return Self {
160                value: value as i64,
161                unit: TimeUnit::Second,
162            };
163        }
164
165        // overflow, return the max of common_time::Duration
166        Self {
167            value: i64::MAX,
168            unit: TimeUnit::Second,
169        }
170    }
171}
172
173impl From<Duration> for std::time::Duration {
174    fn from(d: Duration) -> Self {
175        if d.value < 0 {
176            return std::time::Duration::new(0, 0);
177        }
178        match d.unit {
179            TimeUnit::Nanosecond => std::time::Duration::from_nanos(d.value as u64),
180            TimeUnit::Microsecond => std::time::Duration::from_micros(d.value as u64),
181            TimeUnit::Millisecond => std::time::Duration::from_millis(d.value as u64),
182            TimeUnit::Second => std::time::Duration::from_secs(d.value as u64),
183        }
184    }
185}
186
187impl From<Duration> for serde_json::Value {
188    fn from(d: Duration) -> Self {
189        serde_json::Value::String(d.to_string())
190    }
191}
192
193impl PartialOrd for Duration {
194    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
195        Some(self.cmp(other))
196    }
197}
198
199/// Duration is ordable.
200impl Ord for Duration {
201    fn cmp(&self, other: &Self) -> Ordering {
202        // fast path: most comparisons use the same unit.
203        if self.unit == other.unit {
204            return self.value.cmp(&other.value);
205        }
206
207        let (s_sec, s_nsec) = self.split();
208        let (o_sec, o_nsec) = other.split();
209        match s_sec.cmp(&o_sec) {
210            Ordering::Less => Ordering::Less,
211            Ordering::Greater => Ordering::Greater,
212            Ordering::Equal => s_nsec.cmp(&o_nsec),
213        }
214    }
215}
216
217impl Display for Duration {
218    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
219        write!(f, "{}{}", self.value, self.unit.short_name())
220    }
221}
222
223impl PartialEq for Duration {
224    fn eq(&self, other: &Self) -> bool {
225        self.cmp(other) == Ordering::Equal
226    }
227}
228
229impl Eq for Duration {}
230
231impl Hash for Duration {
232    fn hash<H: Hasher>(&self, state: &mut H) {
233        let (sec, nsec) = self.split();
234        state.write_i64(sec);
235        state.write_u32(nsec);
236    }
237}
238
239#[cfg(test)]
240mod tests {
241
242    use std::collections::hash_map::DefaultHasher;
243    use std::hash::{Hash, Hasher};
244
245    use crate::Duration;
246    use crate::timestamp::TimeUnit;
247
248    #[test]
249    fn test_duration() {
250        let d = Duration::new(1, TimeUnit::Second);
251        assert_eq!(TimeUnit::Second, d.unit());
252        assert_eq!(1, d.value());
253        assert_eq!(Duration::new(1000, TimeUnit::Millisecond), d);
254        assert!(d > Duration::new(999, TimeUnit::Millisecond));
255        assert!(d < Duration::new(1001, TimeUnit::Millisecond));
256    }
257
258    #[test]
259    fn test_cmp_duration() {
260        let d1 = Duration::new(1, TimeUnit::Second);
261        let d2 = Duration::new(1, TimeUnit::Millisecond);
262        assert!(d1 > d2);
263
264        let d1 = Duration::new(1, TimeUnit::Second);
265        let d2 = Duration::new(1, TimeUnit::Microsecond);
266        assert!(d1 > d2);
267
268        let d1 = Duration::new(1, TimeUnit::Second);
269        let d2 = Duration::new(1_000_000_001, TimeUnit::Nanosecond);
270        assert!(d1 < d2);
271
272        let d1 = Duration::new(100, TimeUnit::Millisecond);
273        let d2 = Duration::new(1_000_001, TimeUnit::Microsecond);
274        assert!(d1 < d2);
275
276        let d1 = Duration::new(i64::MAX / 1000, TimeUnit::Second);
277        let d2 = Duration::new(i64::MAX / 1000 * 1000, TimeUnit::Millisecond);
278        assert!(d1 == d2);
279
280        let d1 = Duration::new(i64::MAX / 1000 + 1, TimeUnit::Second);
281        let d2 = Duration::new(i64::MAX / 1000 * 1000, TimeUnit::Millisecond);
282        assert!(d1 > d2);
283
284        let d1 = Duration::new(-100, TimeUnit::Millisecond);
285        let d2 = Duration::new(-100 * 999, TimeUnit::Microsecond);
286        assert!(d1 < d2);
287
288        let d1 = Duration::new(i64::MIN / 1000, TimeUnit::Millisecond);
289        let d2 = Duration::new(i64::MIN / 1000 * 1000, TimeUnit::Microsecond);
290        assert!(d1 == d2);
291    }
292
293    #[test]
294    fn test_convert_i64() {
295        let t = Duration::from(1);
296        assert_eq!(TimeUnit::Millisecond, t.unit());
297        assert_eq!(1, t.value());
298
299        let i: i64 = t.into();
300        assert_eq!(1, i);
301    }
302
303    #[test]
304    fn test_hash() {
305        let check_hash_eq = |d1: Duration, d2: Duration| {
306            let mut hasher = DefaultHasher::new();
307            d1.hash(&mut hasher);
308            let d1_hash = hasher.finish();
309
310            let mut hasher = DefaultHasher::new();
311            d2.hash(&mut hasher);
312            let d2_hash = hasher.finish();
313            d1_hash == d2_hash
314        };
315
316        let d1 = Duration::new(1, TimeUnit::Second);
317        let d2 = Duration::new(1, TimeUnit::Second);
318        assert!(check_hash_eq(d1, d2));
319
320        let d1 = Duration::new(1, TimeUnit::Second);
321        let d2 = Duration::new(1000, TimeUnit::Millisecond);
322        assert!(check_hash_eq(d1, d2));
323
324        let d1 = Duration::new(1, TimeUnit::Second);
325        let d2 = Duration::new(1_000_000, TimeUnit::Microsecond);
326        assert!(check_hash_eq(d1, d2));
327
328        let d1 = Duration::new(1, TimeUnit::Second);
329        let d2 = Duration::new(1_000_000_000, TimeUnit::Nanosecond);
330        assert!(check_hash_eq(d1, d2));
331
332        // not equal
333        let d1 = Duration::new(1, TimeUnit::Second);
334        let d2 = Duration::new(2, TimeUnit::Second);
335        assert!(!check_hash_eq(d1, d2));
336    }
337
338    #[test]
339    fn test_duration_to_string() {
340        let d = Duration::new(1, TimeUnit::Second);
341        assert_eq!("1s", d.to_string());
342
343        let d = Duration::new(2, TimeUnit::Millisecond);
344        assert_eq!("2ms", d.to_string());
345
346        let d = Duration::new(3, TimeUnit::Microsecond);
347        assert_eq!("3us", d.to_string());
348
349        let d = Duration::new(4, TimeUnit::Nanosecond);
350        assert_eq!("4ns", d.to_string());
351    }
352
353    #[test]
354    fn test_serialize_to_json_value() {
355        let d = Duration::new(1, TimeUnit::Second);
356        let json_value = serde_json::to_value(d).unwrap();
357        assert_eq!(
358            json_value,
359            serde_json::json!({"value": 1, "unit": "Second"})
360        );
361
362        let d = Duration::new(1, TimeUnit::Millisecond);
363        let json_value = serde_json::to_value(d).unwrap();
364        assert_eq!(
365            json_value,
366            serde_json::json!({"value": 1, "unit": "Millisecond"})
367        );
368    }
369
370    #[test]
371    fn test_convert_with_std_duration() {
372        // normal test
373        let std_duration = std::time::Duration::new(0, 0);
374        let duration = Duration::from(std_duration);
375        assert_eq!(duration, Duration::new(0, TimeUnit::Nanosecond));
376
377        let std_duration = std::time::Duration::new(1, 0);
378        let duration = Duration::from(std_duration);
379        assert_eq!(duration, Duration::new(1_000_000_000, TimeUnit::Nanosecond));
380
381        let std_duration = std::time::Duration::from_nanos(i64::MAX as u64);
382        let duration = Duration::from(std_duration);
383        assert_eq!(duration, Duration::new(i64::MAX, TimeUnit::Nanosecond));
384
385        let std_duration = std::time::Duration::from_nanos(i64::MAX as u64 + 1);
386        let duration = Duration::from(std_duration);
387        assert_eq!(
388            duration,
389            Duration::new(i64::MAX / 1000, TimeUnit::Microsecond)
390        );
391
392        let std_duration = std::time::Duration::from_nanos(u64::MAX);
393        let duration = Duration::from(std_duration);
394        assert_eq!(
395            duration,
396            Duration::new(18446744073709551, TimeUnit::Microsecond)
397        );
398
399        let std_duration =
400            std::time::Duration::new(i64::MAX as u64 / 1_000, (i64::MAX % 1_000 * 1_000) as u32);
401        let duration = Duration::from(std_duration);
402        assert_eq!(
403            duration,
404            Duration::new(9223372036854775000, TimeUnit::Millisecond)
405        );
406
407        let std_duration = std::time::Duration::new(i64::MAX as u64, 0);
408        let duration = Duration::from(std_duration);
409        assert_eq!(duration, Duration::new(i64::MAX, TimeUnit::Second));
410
411        // max std::time::Duration
412        let std_duration = std::time::Duration::MAX;
413        let duration = Duration::from(std_duration);
414        assert_eq!(
415            duration,
416            Duration::new(9223372036854775807, TimeUnit::Second)
417        );
418
419        // overflow test
420        let std_duration = std::time::Duration::new(i64::MAX as u64, 1);
421        let duration = Duration::from(std_duration);
422        assert_eq!(duration, Duration::new(i64::MAX, TimeUnit::Second));
423
424        // convert back to std::time::Duration
425        let duration = Duration::new(0, TimeUnit::Nanosecond);
426        let std_duration = std::time::Duration::from(duration);
427        assert_eq!(std_duration, std::time::Duration::new(0, 0));
428    }
429}