1use std::fmt::{Display, Formatter};
5
6use serde::{Deserialize, Serialize};
7
8#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub struct Duration {
11 months: i32, days: i32, nanos: i64, }
15
16impl Default for Duration {
17 fn default() -> Self {
18 Self::zero()
19 }
20}
21
22impl Duration {
23 pub fn new(months: i32, days: i32, nanos: i64) -> Self {
24 Self {
25 months,
26 days,
27 nanos,
28 }
29 }
30
31 pub fn from_seconds(seconds: i64) -> Self {
32 Self {
33 months: 0,
34 days: 0,
35 nanos: seconds * 1_000_000_000,
36 }
37 }
38
39 pub fn from_milliseconds(milliseconds: i64) -> Self {
40 Self {
41 months: 0,
42 days: 0,
43 nanos: milliseconds * 1_000_000,
44 }
45 }
46
47 pub fn from_microseconds(microseconds: i64) -> Self {
48 Self {
49 months: 0,
50 days: 0,
51 nanos: microseconds * 1_000,
52 }
53 }
54
55 pub fn from_nanoseconds(nanoseconds: i64) -> Self {
56 Self {
57 months: 0,
58 days: 0,
59 nanos: nanoseconds,
60 }
61 }
62
63 pub fn from_minutes(minutes: i64) -> Self {
64 Self {
65 months: 0,
66 days: 0,
67 nanos: minutes * 60 * 1_000_000_000,
68 }
69 }
70
71 pub fn from_hours(hours: i64) -> Self {
72 Self {
73 months: 0,
74 days: 0,
75 nanos: hours * 60 * 60 * 1_000_000_000,
76 }
77 }
78
79 pub fn from_days(days: i64) -> Self {
80 Self {
81 months: 0,
82 days: days as i32,
83 nanos: 0,
84 }
85 }
86
87 pub fn from_weeks(weeks: i64) -> Self {
88 Self {
89 months: 0,
90 days: (weeks * 7) as i32,
91 nanos: 0,
92 }
93 }
94
95 pub fn from_months(months: i64) -> Self {
96 Self {
97 months: months as i32,
98 days: 0,
99 nanos: 0,
100 }
101 }
102
103 pub fn from_years(years: i64) -> Self {
104 Self {
105 months: (years * 12) as i32,
106 days: 0,
107 nanos: 0,
108 }
109 }
110
111 pub fn zero() -> Self {
112 Self {
113 months: 0,
114 days: 0,
115 nanos: 0,
116 }
117 }
118
119 pub fn seconds(&self) -> i64 {
120 self.nanos / 1_000_000_000
121 }
122
123 pub fn milliseconds(&self) -> i64 {
124 self.nanos / 1_000_000
125 }
126
127 pub fn microseconds(&self) -> i64 {
128 self.nanos / 1_000
129 }
130
131 pub fn nanoseconds(&self) -> i64 {
132 self.nanos
133 }
134
135 pub fn get_months(&self) -> i32 {
136 self.months
137 }
138
139 pub fn get_days(&self) -> i32 {
140 self.days
141 }
142
143 pub fn get_nanos(&self) -> i64 {
144 self.nanos
145 }
146
147 pub fn is_positive(&self) -> bool {
148 self.months > 0 || self.days > 0 || self.nanos > 0
149 }
150
151 pub fn is_negative(&self) -> bool {
152 self.months < 0 || self.days < 0 || self.nanos < 0
153 }
154
155 pub fn abs(&self) -> Self {
156 Self {
157 months: self.months.abs(),
158 days: self.days.abs(),
159 nanos: self.nanos.abs(),
160 }
161 }
162
163 pub fn negate(&self) -> Self {
164 Self {
165 months: -self.months,
166 days: -self.days,
167 nanos: -self.nanos,
168 }
169 }
170}
171
172impl PartialOrd for Duration {
173 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
174 Some(self.cmp(other))
175 }
176}
177
178impl Ord for Duration {
179 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
180 match self.months.cmp(&other.months) {
182 std::cmp::Ordering::Equal => {
183 match self.days.cmp(&other.days) {
185 std::cmp::Ordering::Equal => {
186 self.nanos.cmp(&other.nanos)
188 }
189 other_order => other_order,
190 }
191 }
192 other_order => other_order,
193 }
194 }
195}
196
197impl std::ops::Add for Duration {
198 type Output = Self;
199 fn add(self, rhs: Self) -> Self {
200 Self {
201 months: self.months + rhs.months,
202 days: self.days + rhs.days,
203 nanos: self.nanos + rhs.nanos,
204 }
205 }
206}
207
208impl std::ops::Sub for Duration {
209 type Output = Self;
210 fn sub(self, rhs: Self) -> Self {
211 Self {
212 months: self.months - rhs.months,
213 days: self.days - rhs.days,
214 nanos: self.nanos - rhs.nanos,
215 }
216 }
217}
218
219impl std::ops::Mul<i64> for Duration {
220 type Output = Self;
221 fn mul(self, rhs: i64) -> Self {
222 Self {
223 months: self.months * rhs as i32,
224 days: self.days * rhs as i32,
225 nanos: self.nanos * rhs,
226 }
227 }
228}
229
230impl Display for Duration {
231 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
232 if self.months == 0 && self.days == 0 && self.nanos == 0 {
234 return write!(f, "PT0S");
235 }
236
237 write!(f, "P")?;
238
239 let years = self.months / 12;
241 let months = self.months % 12;
242
243 if years != 0 {
244 write!(f, "{}Y", years)?;
245 }
246
247 if months != 0 {
248 write!(f, "{}M", months)?;
249 }
250
251 let total_seconds = self.nanos / 1_000_000_000;
253 let remaining_nanos = self.nanos % 1_000_000_000;
254
255 let extra_days = total_seconds / 86400; let remaining_seconds = total_seconds % 86400;
258
259 let display_days = self.days + extra_days as i32;
260 let hours = remaining_seconds / 3600;
261 let minutes = (remaining_seconds % 3600) / 60;
262 let seconds = remaining_seconds % 60;
263
264 if display_days != 0 {
265 write!(f, "{}D", display_days)?;
266 }
267
268 if hours != 0 || minutes != 0 || seconds != 0 || remaining_nanos != 0 {
269 write!(f, "T")?;
270
271 if hours != 0 {
272 write!(f, "{}H", hours)?;
273 }
274
275 if minutes != 0 {
276 write!(f, "{}M", minutes)?;
277 }
278
279 if seconds != 0 || remaining_nanos != 0 {
280 if remaining_nanos != 0 {
281 let fractional = remaining_nanos as f64 / 1_000_000_000.0;
284 let total_seconds_f = seconds as f64 + fractional;
285 let formatted_str = format!("{:.9}", total_seconds_f);
288 let formatted = formatted_str.trim_end_matches('0').trim_end_matches('.');
289 write!(f, "{}S", formatted)?;
290 } else {
291 write!(f, "{}S", seconds)?;
292 }
293 }
294 }
295
296 Ok(())
297 }
298}
299
300#[cfg(test)]
301pub mod tests {
302 use super::*;
303
304 #[test]
305 fn test_duration_display_zero() {
306 let duration = Duration::zero();
307 assert_eq!(format!("{}", duration), "PT0S");
308
309 let duration = Duration::from_seconds(0);
310 assert_eq!(format!("{}", duration), "PT0S");
311
312 let duration = Duration::from_nanoseconds(0);
313 assert_eq!(format!("{}", duration), "PT0S");
314
315 let duration = Duration::default();
316 assert_eq!(format!("{}", duration), "PT0S");
317 }
318
319 #[test]
320 fn test_duration_display_seconds_only() {
321 let duration = Duration::from_seconds(1);
322 assert_eq!(format!("{}", duration), "PT1S");
323
324 let duration = Duration::from_seconds(30);
325 assert_eq!(format!("{}", duration), "PT30S");
326
327 let duration = Duration::from_seconds(59);
328 assert_eq!(format!("{}", duration), "PT59S");
329 }
330
331 #[test]
332 fn test_duration_display_minutes_only() {
333 let duration = Duration::from_minutes(1);
334 assert_eq!(format!("{}", duration), "PT1M");
335
336 let duration = Duration::from_minutes(30);
337 assert_eq!(format!("{}", duration), "PT30M");
338
339 let duration = Duration::from_minutes(59);
340 assert_eq!(format!("{}", duration), "PT59M");
341 }
342
343 #[test]
344 fn test_duration_display_hours_only() {
345 let duration = Duration::from_hours(1);
346 assert_eq!(format!("{}", duration), "PT1H");
347
348 let duration = Duration::from_hours(12);
349 assert_eq!(format!("{}", duration), "PT12H");
350
351 let duration = Duration::from_hours(23);
352 assert_eq!(format!("{}", duration), "PT23H");
353 }
354
355 #[test]
356 fn test_duration_display_days_only() {
357 let duration = Duration::from_days(1);
358 assert_eq!(format!("{}", duration), "P1D");
359
360 let duration = Duration::from_days(7);
361 assert_eq!(format!("{}", duration), "P7D");
362
363 let duration = Duration::from_days(365);
364 assert_eq!(format!("{}", duration), "P365D");
365 }
366
367 #[test]
368 fn test_duration_display_weeks_only() {
369 let duration = Duration::from_weeks(1);
370 assert_eq!(format!("{}", duration), "P7D");
371
372 let duration = Duration::from_weeks(2);
373 assert_eq!(format!("{}", duration), "P14D");
374
375 let duration = Duration::from_weeks(52);
376 assert_eq!(format!("{}", duration), "P364D");
377 }
378
379 #[test]
380 fn test_duration_display_combined_time() {
381 let duration = Duration::new(0, 0, (1 * 60 * 60 + 30 * 60) * 1_000_000_000);
383 assert_eq!(format!("{}", duration), "PT1H30M");
384
385 let duration = Duration::new(0, 0, (5 * 60 + 45) * 1_000_000_000);
387 assert_eq!(format!("{}", duration), "PT5M45S");
388
389 let duration = Duration::new(0, 0, (2 * 60 * 60 + 30 * 60 + 45) * 1_000_000_000);
391 assert_eq!(format!("{}", duration), "PT2H30M45S");
392 }
393
394 #[test]
395 fn test_duration_display_combined_date_time() {
396 let duration = Duration::new(0, 1, 2 * 60 * 60 * 1_000_000_000);
398 assert_eq!(format!("{}", duration), "P1DT2H");
399
400 let duration = Duration::new(0, 1, 30 * 60 * 1_000_000_000);
402 assert_eq!(format!("{}", duration), "P1DT30M");
403
404 let duration = Duration::new(0, 1, (2 * 60 * 60 + 30 * 60) * 1_000_000_000);
406 assert_eq!(format!("{}", duration), "P1DT2H30M");
407
408 let duration = Duration::new(0, 1, (2 * 60 * 60 + 30 * 60 + 45) * 1_000_000_000);
410 assert_eq!(format!("{}", duration), "P1DT2H30M45S");
411 }
412
413 #[test]
414 fn test_duration_display_milliseconds() {
415 let duration = Duration::from_milliseconds(123);
416 assert_eq!(format!("{}", duration), "PT0.123S");
417
418 let duration = Duration::from_milliseconds(1);
419 assert_eq!(format!("{}", duration), "PT0.001S");
420
421 let duration = Duration::from_milliseconds(999);
422 assert_eq!(format!("{}", duration), "PT0.999S");
423
424 let duration = Duration::from_milliseconds(1500);
425 assert_eq!(format!("{}", duration), "PT1.5S");
426 }
427
428 #[test]
429 fn test_duration_display_microseconds() {
430 let duration = Duration::from_microseconds(123456);
431 assert_eq!(format!("{}", duration), "PT0.123456S");
432
433 let duration = Duration::from_microseconds(1);
434 assert_eq!(format!("{}", duration), "PT0.000001S");
435
436 let duration = Duration::from_microseconds(999999);
437 assert_eq!(format!("{}", duration), "PT0.999999S");
438
439 let duration = Duration::from_microseconds(1500000);
440 assert_eq!(format!("{}", duration), "PT1.5S");
441 }
442
443 #[test]
444 fn test_duration_display_nanoseconds() {
445 let duration = Duration::from_nanoseconds(123456789);
446 assert_eq!(format!("{}", duration), "PT0.123456789S");
447
448 let duration = Duration::from_nanoseconds(1);
449 assert_eq!(format!("{}", duration), "PT0.000000001S");
450
451 let duration = Duration::from_nanoseconds(999999999);
452 assert_eq!(format!("{}", duration), "PT0.999999999S");
453
454 let duration = Duration::from_nanoseconds(1500000000);
455 assert_eq!(format!("{}", duration), "PT1.5S");
456 }
457
458 #[test]
459 fn test_duration_display_fractional_seconds_with_integers() {
460 let duration = Duration::new(0, 0, 1 * 1_000_000_000 + 500 * 1_000_000);
462 assert_eq!(format!("{}", duration), "PT1.5S");
463
464 let duration = Duration::new(0, 0, 2 * 1_000_000_000 + 123456 * 1_000);
466 assert_eq!(format!("{}", duration), "PT2.123456S");
467
468 let duration = Duration::new(0, 0, 3 * 1_000_000_000 + 123456789);
470 assert_eq!(format!("{}", duration), "PT3.123456789S");
471 }
472
473 #[test]
474 fn test_duration_display_comptokenize_durations() {
475 let duration = Duration::new(0, 1, (2 * 60 * 60 + 30 * 60 + 45) * 1_000_000_000 + 123 * 1_000_000);
477 assert_eq!(format!("{}", duration), "P1DT2H30M45.123S");
478
479 let duration = Duration::new(0, 7, (12 * 60 * 60 + 45 * 60 + 30) * 1_000_000_000 + 456789 * 1_000);
481 assert_eq!(format!("{}", duration), "P7DT12H45M30.456789S");
482 }
483
484 #[test]
485 fn test_duration_display_trailing_zeros_removed() {
486 let duration = Duration::from_nanoseconds(100000000); assert_eq!(format!("{}", duration), "PT0.1S");
489
490 let duration = Duration::from_nanoseconds(120000000); assert_eq!(format!("{}", duration), "PT0.12S");
492
493 let duration = Duration::from_nanoseconds(123000000); assert_eq!(format!("{}", duration), "PT0.123S");
495
496 let duration = Duration::from_nanoseconds(123400000); assert_eq!(format!("{}", duration), "PT0.1234S");
498
499 let duration = Duration::from_nanoseconds(123450000); assert_eq!(format!("{}", duration), "PT0.12345S");
501
502 let duration = Duration::from_nanoseconds(123456000); assert_eq!(format!("{}", duration), "PT0.123456S");
504
505 let duration = Duration::from_nanoseconds(123456700); assert_eq!(format!("{}", duration), "PT0.1234567S");
507
508 let duration = Duration::from_nanoseconds(123456780); assert_eq!(format!("{}", duration), "PT0.12345678S");
510
511 let duration = Duration::from_nanoseconds(123456789); assert_eq!(format!("{}", duration), "PT0.123456789S");
513 }
514
515 #[test]
516 fn test_duration_display_negative_durations() {
517 let duration = Duration::from_seconds(-30);
519 assert_eq!(format!("{}", duration), "PT-30S");
520
521 let duration = Duration::from_minutes(-5);
522 assert_eq!(format!("{}", duration), "PT-5M");
523
524 let duration = Duration::from_hours(-2);
525 assert_eq!(format!("{}", duration), "PT-2H");
526
527 let duration = Duration::from_days(-1);
528 assert_eq!(format!("{}", duration), "P-1D");
529 }
530
531 #[test]
532 fn test_duration_display_large_values() {
533 let duration = Duration::from_days(1000);
535 assert_eq!(format!("{}", duration), "P1000D");
536
537 let duration = Duration::from_hours(25);
538 assert_eq!(format!("{}", duration), "P1DT1H");
539
540 let duration = Duration::from_minutes(1500); assert_eq!(format!("{}", duration), "P1DT1H");
542
543 let duration = Duration::from_seconds(90000); assert_eq!(format!("{}", duration), "P1DT1H");
545 }
546
547 #[test]
548 fn test_duration_display_edge_cases() {
549 let duration = Duration::from_nanoseconds(1);
551 assert_eq!(format!("{}", duration), "PT0.000000001S");
552
553 let duration = Duration::from_nanoseconds(999999999);
555 assert_eq!(format!("{}", duration), "PT0.999999999S");
556
557 let duration = Duration::from_nanoseconds(1000000000);
559 assert_eq!(format!("{}", duration), "PT1S");
560
561 let duration = Duration::from_nanoseconds(60 * 1000000000);
563 assert_eq!(format!("{}", duration), "PT1M");
564
565 let duration = Duration::from_nanoseconds(3600 * 1000000000);
567 assert_eq!(format!("{}", duration), "PT1H");
568
569 let duration = Duration::from_nanoseconds(86400 * 1000000000);
571 assert_eq!(format!("{}", duration), "P1D");
572 }
573
574 #[test]
575 fn test_duration_display_precision_boundaries() {
576 let duration = Duration::from_nanoseconds(100); assert_eq!(format!("{}", duration), "PT0.0000001S");
579
580 let duration = Duration::from_nanoseconds(10); assert_eq!(format!("{}", duration), "PT0.00000001S");
582
583 let duration = Duration::from_nanoseconds(1); assert_eq!(format!("{}", duration), "PT0.000000001S");
585 }
586
587 #[test]
588 fn test_duration_display_from_nanos() {
589 let duration = Duration::from_nanoseconds(123456789);
591 assert_eq!(format!("{}", duration), "PT0.123456789S");
592
593 let duration = Duration::from_nanoseconds(3661000000000); assert_eq!(format!("{}", duration), "PT1H1M1S");
595 }
596
597 #[test]
598 fn test_duration_display_abs_and_negate() {
599 let duration = Duration::from_seconds(-30);
601 let abs_duration = duration.abs();
602 assert_eq!(format!("{}", abs_duration), "PT30S");
603
604 let duration = Duration::from_seconds(30);
606 let neg_duration = duration.negate();
607 assert_eq!(format!("{}", neg_duration), "PT-30S");
608 }
609}