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