1use 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#[derive(Debug, Clone, Default, Copy, Serialize, Deserialize)]
25pub struct Duration {
26 value: i64,
27 unit: TimeUnit,
28}
29
30impl Duration {
31 pub fn new(value: i64, unit: TimeUnit) -> Self {
33 Self { value, unit }
34 }
35
36 pub fn new_second(value: i64) -> Self {
38 Self {
39 value,
40 unit: TimeUnit::Second,
41 }
42 }
43
44 pub fn new_millisecond(value: i64) -> Self {
46 Self {
47 value,
48 unit: TimeUnit::Millisecond,
49 }
50 }
51
52 pub fn new_microsecond(value: i64) -> Self {
54 Self {
55 value,
56 unit: TimeUnit::Microsecond,
57 }
58 }
59
60 pub fn new_nanosecond(value: i64) -> Self {
62 Self {
63 value,
64 unit: TimeUnit::Nanosecond,
65 }
66 }
67
68 pub fn unit(&self) -> TimeUnit {
70 self.unit
71 }
72
73 pub fn value(&self) -> i64 {
75 self.value
76 }
77
78 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 let nsec = u32::try_from(sec_mod * nsec_mul).unwrap();
88 (sec_div, nsec)
89 }
90
91 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
107impl From<i64> for Duration {
110 fn from(v: i64) -> Self {
111 Self {
112 value: v,
113 unit: TimeUnit::Millisecond,
114 }
115 }
116}
117
118impl From<Duration> for i64 {
120 fn from(d: Duration) -> Self {
121 d.value
122 }
123}
124
125impl From<std::time::Duration> for Duration {
131 fn from(d: std::time::Duration) -> Self {
132 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 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
199impl Ord for Duration {
201 fn cmp(&self, other: &Self) -> Ordering {
202 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 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 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 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 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 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}