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
102impl From<i64> for Duration {
105 fn from(v: i64) -> Self {
106 Self {
107 value: v,
108 unit: TimeUnit::Millisecond,
109 }
110 }
111}
112
113impl From<Duration> for i64 {
115 fn from(d: Duration) -> Self {
116 d.value
117 }
118}
119
120impl From<std::time::Duration> for Duration {
126 fn from(d: std::time::Duration) -> Self {
127 let value = d.as_nanos();
129 if value <= i64::MAX as u128 {
130 return Self {
131 value: value as i64,
132 unit: TimeUnit::Nanosecond,
133 };
134 }
135
136 let value = d.as_micros();
137 if value <= i64::MAX as u128 {
138 return Self {
139 value: value as i64,
140 unit: TimeUnit::Microsecond,
141 };
142 }
143
144 let value = d.as_millis();
145 if value <= i64::MAX as u128 {
146 return Self {
147 value: value as i64,
148 unit: TimeUnit::Millisecond,
149 };
150 }
151
152 let value = d.as_secs();
153 if value <= i64::MAX as u64 {
154 return Self {
155 value: value as i64,
156 unit: TimeUnit::Second,
157 };
158 }
159
160 Self {
162 value: i64::MAX,
163 unit: TimeUnit::Second,
164 }
165 }
166}
167
168impl From<Duration> for std::time::Duration {
169 fn from(d: Duration) -> Self {
170 if d.value < 0 {
171 return std::time::Duration::new(0, 0);
172 }
173 match d.unit {
174 TimeUnit::Nanosecond => std::time::Duration::from_nanos(d.value as u64),
175 TimeUnit::Microsecond => std::time::Duration::from_micros(d.value as u64),
176 TimeUnit::Millisecond => std::time::Duration::from_millis(d.value as u64),
177 TimeUnit::Second => std::time::Duration::from_secs(d.value as u64),
178 }
179 }
180}
181
182impl From<Duration> for serde_json::Value {
183 fn from(d: Duration) -> Self {
184 serde_json::Value::String(d.to_string())
185 }
186}
187
188impl PartialOrd for Duration {
189 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
190 Some(self.cmp(other))
191 }
192}
193
194impl Ord for Duration {
196 fn cmp(&self, other: &Self) -> Ordering {
197 if self.unit == other.unit {
199 return self.value.cmp(&other.value);
200 }
201
202 let (s_sec, s_nsec) = self.split();
203 let (o_sec, o_nsec) = other.split();
204 match s_sec.cmp(&o_sec) {
205 Ordering::Less => Ordering::Less,
206 Ordering::Greater => Ordering::Greater,
207 Ordering::Equal => s_nsec.cmp(&o_nsec),
208 }
209 }
210}
211
212impl Display for Duration {
213 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
214 write!(f, "{}{}", self.value, self.unit.short_name())
215 }
216}
217
218impl PartialEq for Duration {
219 fn eq(&self, other: &Self) -> bool {
220 self.cmp(other) == Ordering::Equal
221 }
222}
223
224impl Eq for Duration {}
225
226impl Hash for Duration {
227 fn hash<H: Hasher>(&self, state: &mut H) {
228 let (sec, nsec) = self.split();
229 state.write_i64(sec);
230 state.write_u32(nsec);
231 }
232}
233
234#[cfg(test)]
235mod tests {
236
237 use std::collections::hash_map::DefaultHasher;
238 use std::hash::{Hash, Hasher};
239
240 use crate::timestamp::TimeUnit;
241 use crate::Duration;
242
243 #[test]
244 fn test_duration() {
245 let d = Duration::new(1, TimeUnit::Second);
246 assert_eq!(TimeUnit::Second, d.unit());
247 assert_eq!(1, d.value());
248 assert_eq!(Duration::new(1000, TimeUnit::Millisecond), d);
249 assert!(d > Duration::new(999, TimeUnit::Millisecond));
250 assert!(d < Duration::new(1001, TimeUnit::Millisecond));
251 }
252
253 #[test]
254 fn test_cmp_duration() {
255 let d1 = Duration::new(1, TimeUnit::Second);
256 let d2 = Duration::new(1, TimeUnit::Millisecond);
257 assert!(d1 > d2);
258
259 let d1 = Duration::new(1, TimeUnit::Second);
260 let d2 = Duration::new(1, TimeUnit::Microsecond);
261 assert!(d1 > d2);
262
263 let d1 = Duration::new(1, TimeUnit::Second);
264 let d2 = Duration::new(1_000_000_001, TimeUnit::Nanosecond);
265 assert!(d1 < d2);
266
267 let d1 = Duration::new(100, TimeUnit::Millisecond);
268 let d2 = Duration::new(1_000_001, TimeUnit::Microsecond);
269 assert!(d1 < d2);
270
271 let d1 = Duration::new(i64::MAX / 1000, TimeUnit::Second);
272 let d2 = Duration::new(i64::MAX / 1000 * 1000, TimeUnit::Millisecond);
273 assert!(d1 == d2);
274
275 let d1 = Duration::new(i64::MAX / 1000 + 1, TimeUnit::Second);
276 let d2 = Duration::new(i64::MAX / 1000 * 1000, TimeUnit::Millisecond);
277 assert!(d1 > d2);
278
279 let d1 = Duration::new(-100, TimeUnit::Millisecond);
280 let d2 = Duration::new(-100 * 999, TimeUnit::Microsecond);
281 assert!(d1 < d2);
282
283 let d1 = Duration::new(i64::MIN / 1000, TimeUnit::Millisecond);
284 let d2 = Duration::new(i64::MIN / 1000 * 1000, TimeUnit::Microsecond);
285 assert!(d1 == d2);
286 }
287
288 #[test]
289 fn test_convert_i64() {
290 let t = Duration::from(1);
291 assert_eq!(TimeUnit::Millisecond, t.unit());
292 assert_eq!(1, t.value());
293
294 let i: i64 = t.into();
295 assert_eq!(1, i);
296 }
297
298 #[test]
299 fn test_hash() {
300 let check_hash_eq = |d1: Duration, d2: Duration| {
301 let mut hasher = DefaultHasher::new();
302 d1.hash(&mut hasher);
303 let d1_hash = hasher.finish();
304
305 let mut hasher = DefaultHasher::new();
306 d2.hash(&mut hasher);
307 let d2_hash = hasher.finish();
308 d1_hash == d2_hash
309 };
310
311 let d1 = Duration::new(1, TimeUnit::Second);
312 let d2 = Duration::new(1, TimeUnit::Second);
313 assert!(check_hash_eq(d1, d2));
314
315 let d1 = Duration::new(1, TimeUnit::Second);
316 let d2 = Duration::new(1000, TimeUnit::Millisecond);
317 assert!(check_hash_eq(d1, d2));
318
319 let d1 = Duration::new(1, TimeUnit::Second);
320 let d2 = Duration::new(1_000_000, TimeUnit::Microsecond);
321 assert!(check_hash_eq(d1, d2));
322
323 let d1 = Duration::new(1, TimeUnit::Second);
324 let d2 = Duration::new(1_000_000_000, TimeUnit::Nanosecond);
325 assert!(check_hash_eq(d1, d2));
326
327 let d1 = Duration::new(1, TimeUnit::Second);
329 let d2 = Duration::new(2, TimeUnit::Second);
330 assert!(!check_hash_eq(d1, d2));
331 }
332
333 #[test]
334 fn test_duration_to_string() {
335 let d = Duration::new(1, TimeUnit::Second);
336 assert_eq!("1s", d.to_string());
337
338 let d = Duration::new(2, TimeUnit::Millisecond);
339 assert_eq!("2ms", d.to_string());
340
341 let d = Duration::new(3, TimeUnit::Microsecond);
342 assert_eq!("3us", d.to_string());
343
344 let d = Duration::new(4, TimeUnit::Nanosecond);
345 assert_eq!("4ns", d.to_string());
346 }
347
348 #[test]
349 fn test_serialize_to_json_value() {
350 let d = Duration::new(1, TimeUnit::Second);
351 let json_value = serde_json::to_value(d).unwrap();
352 assert_eq!(
353 json_value,
354 serde_json::json!({"value": 1, "unit": "Second"})
355 );
356
357 let d = Duration::new(1, TimeUnit::Millisecond);
358 let json_value = serde_json::to_value(d).unwrap();
359 assert_eq!(
360 json_value,
361 serde_json::json!({"value": 1, "unit": "Millisecond"})
362 );
363 }
364
365 #[test]
366 fn test_convert_with_std_duration() {
367 let std_duration = std::time::Duration::new(0, 0);
369 let duration = Duration::from(std_duration);
370 assert_eq!(duration, Duration::new(0, TimeUnit::Nanosecond));
371
372 let std_duration = std::time::Duration::new(1, 0);
373 let duration = Duration::from(std_duration);
374 assert_eq!(duration, Duration::new(1_000_000_000, TimeUnit::Nanosecond));
375
376 let std_duration = std::time::Duration::from_nanos(i64::MAX as u64);
377 let duration = Duration::from(std_duration);
378 assert_eq!(duration, Duration::new(i64::MAX, TimeUnit::Nanosecond));
379
380 let std_duration = std::time::Duration::from_nanos(i64::MAX as u64 + 1);
381 let duration = Duration::from(std_duration);
382 assert_eq!(
383 duration,
384 Duration::new(i64::MAX / 1000, TimeUnit::Microsecond)
385 );
386
387 let std_duration = std::time::Duration::from_nanos(u64::MAX);
388 let duration = Duration::from(std_duration);
389 assert_eq!(
390 duration,
391 Duration::new(18446744073709551, TimeUnit::Microsecond)
392 );
393
394 let std_duration =
395 std::time::Duration::new(i64::MAX as u64 / 1_000, (i64::MAX % 1_000 * 1_000) as u32);
396 let duration = Duration::from(std_duration);
397 assert_eq!(
398 duration,
399 Duration::new(9223372036854775000, TimeUnit::Millisecond)
400 );
401
402 let std_duration = std::time::Duration::new(i64::MAX as u64, 0);
403 let duration = Duration::from(std_duration);
404 assert_eq!(duration, Duration::new(i64::MAX, TimeUnit::Second));
405
406 let std_duration = std::time::Duration::MAX;
408 let duration = Duration::from(std_duration);
409 assert_eq!(
410 duration,
411 Duration::new(9223372036854775807, TimeUnit::Second)
412 );
413
414 let std_duration = std::time::Duration::new(i64::MAX as u64, 1);
416 let duration = Duration::from(std_duration);
417 assert_eq!(duration, Duration::new(i64::MAX, TimeUnit::Second));
418
419 let duration = Duration::new(0, TimeUnit::Nanosecond);
421 let std_duration = std::time::Duration::from(duration);
422 assert_eq!(std_duration, std::time::Duration::new(0, 0));
423 }
424}