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 Display for Duration {
198 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
199 if self.months == 0 && self.days == 0 && self.nanos == 0 {
201 return write!(f, "PT0S");
202 }
203
204 write!(f, "P")?;
205
206 let years = self.months / 12;
208 let months = self.months % 12;
209
210 if years != 0 {
211 write!(f, "{}Y", years)?;
212 }
213
214 if months != 0 {
215 write!(f, "{}M", months)?;
216 }
217
218 let total_seconds = self.nanos / 1_000_000_000;
220 let remaining_nanos = self.nanos % 1_000_000_000;
221
222 let extra_days = total_seconds / 86400; let remaining_seconds = total_seconds % 86400;
225
226 let display_days = self.days + extra_days as i32;
227 let hours = remaining_seconds / 3600;
228 let minutes = (remaining_seconds % 3600) / 60;
229 let seconds = remaining_seconds % 60;
230
231 if display_days != 0 {
232 write!(f, "{}D", display_days)?;
233 }
234
235 if hours != 0 || minutes != 0 || seconds != 0 || remaining_nanos != 0 {
236 write!(f, "T")?;
237
238 if hours != 0 {
239 write!(f, "{}H", hours)?;
240 }
241
242 if minutes != 0 {
243 write!(f, "{}M", minutes)?;
244 }
245
246 if seconds != 0 || remaining_nanos != 0 {
247 if remaining_nanos != 0 {
248 let fractional = remaining_nanos as f64 / 1_000_000_000.0;
251 let total_seconds_f = seconds as f64 + fractional;
252 let formatted_str = format!("{:.9}", total_seconds_f);
255 let formatted = formatted_str.trim_end_matches('0').trim_end_matches('.');
256 write!(f, "{}S", formatted)?;
257 } else {
258 write!(f, "{}S", seconds)?;
259 }
260 }
261 }
262
263 Ok(())
264 }
265}
266
267#[cfg(test)]
268mod tests {
269 use super::*;
270
271 #[test]
272 fn test_duration_display_zero() {
273 let duration = Duration::zero();
274 assert_eq!(format!("{}", duration), "PT0S");
275
276 let duration = Duration::from_seconds(0);
277 assert_eq!(format!("{}", duration), "PT0S");
278
279 let duration = Duration::from_nanoseconds(0);
280 assert_eq!(format!("{}", duration), "PT0S");
281
282 let duration = Duration::default();
283 assert_eq!(format!("{}", duration), "PT0S");
284 }
285
286 #[test]
287 fn test_duration_display_seconds_only() {
288 let duration = Duration::from_seconds(1);
289 assert_eq!(format!("{}", duration), "PT1S");
290
291 let duration = Duration::from_seconds(30);
292 assert_eq!(format!("{}", duration), "PT30S");
293
294 let duration = Duration::from_seconds(59);
295 assert_eq!(format!("{}", duration), "PT59S");
296 }
297
298 #[test]
299 fn test_duration_display_minutes_only() {
300 let duration = Duration::from_minutes(1);
301 assert_eq!(format!("{}", duration), "PT1M");
302
303 let duration = Duration::from_minutes(30);
304 assert_eq!(format!("{}", duration), "PT30M");
305
306 let duration = Duration::from_minutes(59);
307 assert_eq!(format!("{}", duration), "PT59M");
308 }
309
310 #[test]
311 fn test_duration_display_hours_only() {
312 let duration = Duration::from_hours(1);
313 assert_eq!(format!("{}", duration), "PT1H");
314
315 let duration = Duration::from_hours(12);
316 assert_eq!(format!("{}", duration), "PT12H");
317
318 let duration = Duration::from_hours(23);
319 assert_eq!(format!("{}", duration), "PT23H");
320 }
321
322 #[test]
323 fn test_duration_display_days_only() {
324 let duration = Duration::from_days(1);
325 assert_eq!(format!("{}", duration), "P1D");
326
327 let duration = Duration::from_days(7);
328 assert_eq!(format!("{}", duration), "P7D");
329
330 let duration = Duration::from_days(365);
331 assert_eq!(format!("{}", duration), "P365D");
332 }
333
334 #[test]
335 fn test_duration_display_weeks_only() {
336 let duration = Duration::from_weeks(1);
337 assert_eq!(format!("{}", duration), "P7D");
338
339 let duration = Duration::from_weeks(2);
340 assert_eq!(format!("{}", duration), "P14D");
341
342 let duration = Duration::from_weeks(52);
343 assert_eq!(format!("{}", duration), "P364D");
344 }
345
346 #[test]
347 fn test_duration_display_combined_time() {
348 let duration = Duration::new(0, 0, (1 * 60 * 60 + 30 * 60) * 1_000_000_000);
350 assert_eq!(format!("{}", duration), "PT1H30M");
351
352 let duration = Duration::new(0, 0, (5 * 60 + 45) * 1_000_000_000);
354 assert_eq!(format!("{}", duration), "PT5M45S");
355
356 let duration = Duration::new(0, 0, (2 * 60 * 60 + 30 * 60 + 45) * 1_000_000_000);
358 assert_eq!(format!("{}", duration), "PT2H30M45S");
359 }
360
361 #[test]
362 fn test_duration_display_combined_date_time() {
363 let duration = Duration::new(0, 1, 2 * 60 * 60 * 1_000_000_000);
365 assert_eq!(format!("{}", duration), "P1DT2H");
366
367 let duration = Duration::new(0, 1, 30 * 60 * 1_000_000_000);
369 assert_eq!(format!("{}", duration), "P1DT30M");
370
371 let duration = Duration::new(0, 1, (2 * 60 * 60 + 30 * 60) * 1_000_000_000);
373 assert_eq!(format!("{}", duration), "P1DT2H30M");
374
375 let duration = Duration::new(0, 1, (2 * 60 * 60 + 30 * 60 + 45) * 1_000_000_000);
377 assert_eq!(format!("{}", duration), "P1DT2H30M45S");
378 }
379
380 #[test]
381 fn test_duration_display_milliseconds() {
382 let duration = Duration::from_milliseconds(123);
383 assert_eq!(format!("{}", duration), "PT0.123S");
384
385 let duration = Duration::from_milliseconds(1);
386 assert_eq!(format!("{}", duration), "PT0.001S");
387
388 let duration = Duration::from_milliseconds(999);
389 assert_eq!(format!("{}", duration), "PT0.999S");
390
391 let duration = Duration::from_milliseconds(1500);
392 assert_eq!(format!("{}", duration), "PT1.5S");
393 }
394
395 #[test]
396 fn test_duration_display_microseconds() {
397 let duration = Duration::from_microseconds(123456);
398 assert_eq!(format!("{}", duration), "PT0.123456S");
399
400 let duration = Duration::from_microseconds(1);
401 assert_eq!(format!("{}", duration), "PT0.000001S");
402
403 let duration = Duration::from_microseconds(999999);
404 assert_eq!(format!("{}", duration), "PT0.999999S");
405
406 let duration = Duration::from_microseconds(1500000);
407 assert_eq!(format!("{}", duration), "PT1.5S");
408 }
409
410 #[test]
411 fn test_duration_display_nanoseconds() {
412 let duration = Duration::from_nanoseconds(123456789);
413 assert_eq!(format!("{}", duration), "PT0.123456789S");
414
415 let duration = Duration::from_nanoseconds(1);
416 assert_eq!(format!("{}", duration), "PT0.000000001S");
417
418 let duration = Duration::from_nanoseconds(999999999);
419 assert_eq!(format!("{}", duration), "PT0.999999999S");
420
421 let duration = Duration::from_nanoseconds(1500000000);
422 assert_eq!(format!("{}", duration), "PT1.5S");
423 }
424
425 #[test]
426 fn test_duration_display_fractional_seconds_with_integers() {
427 let duration = Duration::new(0, 0, 1 * 1_000_000_000 + 500 * 1_000_000);
429 assert_eq!(format!("{}", duration), "PT1.5S");
430
431 let duration = Duration::new(0, 0, 2 * 1_000_000_000 + 123456 * 1_000);
433 assert_eq!(format!("{}", duration), "PT2.123456S");
434
435 let duration = Duration::new(0, 0, 3 * 1_000_000_000 + 123456789);
437 assert_eq!(format!("{}", duration), "PT3.123456789S");
438 }
439
440 #[test]
441 fn test_duration_display_comptokenize_durations() {
442 let duration = Duration::new(0, 1, (2 * 60 * 60 + 30 * 60 + 45) * 1_000_000_000 + 123 * 1_000_000);
444 assert_eq!(format!("{}", duration), "P1DT2H30M45.123S");
445
446 let duration = Duration::new(0, 7, (12 * 60 * 60 + 45 * 60 + 30) * 1_000_000_000 + 456789 * 1_000);
448 assert_eq!(format!("{}", duration), "P7DT12H45M30.456789S");
449 }
450
451 #[test]
452 fn test_duration_display_trailing_zeros_removed() {
453 let duration = Duration::from_nanoseconds(100000000); assert_eq!(format!("{}", duration), "PT0.1S");
456
457 let duration = Duration::from_nanoseconds(120000000); assert_eq!(format!("{}", duration), "PT0.12S");
459
460 let duration = Duration::from_nanoseconds(123000000); assert_eq!(format!("{}", duration), "PT0.123S");
462
463 let duration = Duration::from_nanoseconds(123400000); assert_eq!(format!("{}", duration), "PT0.1234S");
465
466 let duration = Duration::from_nanoseconds(123450000); assert_eq!(format!("{}", duration), "PT0.12345S");
468
469 let duration = Duration::from_nanoseconds(123456000); assert_eq!(format!("{}", duration), "PT0.123456S");
471
472 let duration = Duration::from_nanoseconds(123456700); assert_eq!(format!("{}", duration), "PT0.1234567S");
474
475 let duration = Duration::from_nanoseconds(123456780); assert_eq!(format!("{}", duration), "PT0.12345678S");
477
478 let duration = Duration::from_nanoseconds(123456789); assert_eq!(format!("{}", duration), "PT0.123456789S");
480 }
481
482 #[test]
483 fn test_duration_display_negative_durations() {
484 let duration = Duration::from_seconds(-30);
486 assert_eq!(format!("{}", duration), "PT-30S");
487
488 let duration = Duration::from_minutes(-5);
489 assert_eq!(format!("{}", duration), "PT-5M");
490
491 let duration = Duration::from_hours(-2);
492 assert_eq!(format!("{}", duration), "PT-2H");
493
494 let duration = Duration::from_days(-1);
495 assert_eq!(format!("{}", duration), "P-1D");
496 }
497
498 #[test]
499 fn test_duration_display_large_values() {
500 let duration = Duration::from_days(1000);
502 assert_eq!(format!("{}", duration), "P1000D");
503
504 let duration = Duration::from_hours(25);
505 assert_eq!(format!("{}", duration), "P1DT1H");
506
507 let duration = Duration::from_minutes(1500); assert_eq!(format!("{}", duration), "P1DT1H");
509
510 let duration = Duration::from_seconds(90000); assert_eq!(format!("{}", duration), "P1DT1H");
512 }
513
514 #[test]
515 fn test_duration_display_edge_cases() {
516 let duration = Duration::from_nanoseconds(1);
518 assert_eq!(format!("{}", duration), "PT0.000000001S");
519
520 let duration = Duration::from_nanoseconds(999999999);
522 assert_eq!(format!("{}", duration), "PT0.999999999S");
523
524 let duration = Duration::from_nanoseconds(1000000000);
526 assert_eq!(format!("{}", duration), "PT1S");
527
528 let duration = Duration::from_nanoseconds(60 * 1000000000);
530 assert_eq!(format!("{}", duration), "PT1M");
531
532 let duration = Duration::from_nanoseconds(3600 * 1000000000);
534 assert_eq!(format!("{}", duration), "PT1H");
535
536 let duration = Duration::from_nanoseconds(86400 * 1000000000);
538 assert_eq!(format!("{}", duration), "P1D");
539 }
540
541 #[test]
542 fn test_duration_display_precision_boundaries() {
543 let duration = Duration::from_nanoseconds(100); assert_eq!(format!("{}", duration), "PT0.0000001S");
546
547 let duration = Duration::from_nanoseconds(10); assert_eq!(format!("{}", duration), "PT0.00000001S");
549
550 let duration = Duration::from_nanoseconds(1); assert_eq!(format!("{}", duration), "PT0.000000001S");
552 }
553
554 #[test]
555 fn test_duration_display_from_nanos() {
556 let duration = Duration::from_nanoseconds(123456789);
558 assert_eq!(format!("{}", duration), "PT0.123456789S");
559
560 let duration = Duration::from_nanoseconds(3661000000000); assert_eq!(format!("{}", duration), "PT1H1M1S");
562 }
563
564 #[test]
565 fn test_duration_display_abs_and_negate() {
566 let duration = Duration::from_seconds(-30);
568 let abs_duration = duration.abs();
569 assert_eq!(format!("{}", abs_duration), "PT30S");
570
571 let duration = Duration::from_seconds(30);
573 let neg_duration = duration.negate();
574 assert_eq!(format!("{}", neg_duration), "PT-30S");
575 }
576}