1use std::{
5 cmp,
6 fmt::{self, Display, Formatter, Write},
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 pub fn to_iso_string(&self) -> String {
177 if self.months == 0 && self.days == 0 && self.nanos == 0 {
178 return "PT0S".to_string();
179 }
180
181 let mut result = String::from("P");
182
183 let years = self.months / 12;
184 let months = self.months % 12;
185
186 if years != 0 {
187 write!(result, "{}Y", years).unwrap();
188 }
189 if months != 0 {
190 write!(result, "{}M", months).unwrap();
191 }
192
193 let total_seconds = self.nanos / 1_000_000_000;
194 let remaining_nanos = self.nanos % 1_000_000_000;
195
196 let extra_days = total_seconds / 86400;
197 let remaining_seconds = total_seconds % 86400;
198
199 let display_days = self.days + extra_days as i32;
200 let hours = remaining_seconds / 3600;
201 let minutes = (remaining_seconds % 3600) / 60;
202 let seconds = remaining_seconds % 60;
203
204 if display_days != 0 {
205 write!(result, "{}D", display_days).unwrap();
206 }
207
208 if hours != 0 || minutes != 0 || seconds != 0 || remaining_nanos != 0 {
209 result.push('T');
210
211 if hours != 0 {
212 write!(result, "{}H", hours).unwrap();
213 }
214 if minutes != 0 {
215 write!(result, "{}M", minutes).unwrap();
216 }
217 if seconds != 0 || remaining_nanos != 0 {
218 if remaining_nanos != 0 {
219 let fractional = remaining_nanos as f64 / 1_000_000_000.0;
220 let total_seconds_f = seconds as f64 + fractional;
221 let formatted_str = format!("{:.9}", total_seconds_f);
222 let formatted = formatted_str.trim_end_matches('0').trim_end_matches('.');
223 write!(result, "{}S", formatted).unwrap();
224 } else {
225 write!(result, "{}S", seconds).unwrap();
226 }
227 }
228 }
229
230 result
231 }
232}
233
234impl PartialOrd for Duration {
235 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
236 Some(self.cmp(other))
237 }
238}
239
240impl Ord for Duration {
241 fn cmp(&self, other: &Self) -> cmp::Ordering {
242 match self.months.cmp(&other.months) {
244 cmp::Ordering::Equal => {
245 match self.days.cmp(&other.days) {
247 cmp::Ordering::Equal => {
248 self.nanos.cmp(&other.nanos)
250 }
251 other_order => other_order,
252 }
253 }
254 other_order => other_order,
255 }
256 }
257}
258
259impl ops::Add for Duration {
260 type Output = Self;
261 fn add(self, rhs: Self) -> Self {
262 Self {
263 months: self.months + rhs.months,
264 days: self.days + rhs.days,
265 nanos: self.nanos + rhs.nanos,
266 }
267 }
268}
269
270impl ops::Sub for Duration {
271 type Output = Self;
272 fn sub(self, rhs: Self) -> Self {
273 Self {
274 months: self.months - rhs.months,
275 days: self.days - rhs.days,
276 nanos: self.nanos - rhs.nanos,
277 }
278 }
279}
280
281impl ops::Mul<i64> for Duration {
282 type Output = Self;
283 fn mul(self, rhs: i64) -> Self {
284 Self {
285 months: self.months * rhs as i32,
286 days: self.days * rhs as i32,
287 nanos: self.nanos * rhs,
288 }
289 }
290}
291
292impl Display for Duration {
293 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
294 if self.months == 0 && self.days == 0 && self.nanos == 0 {
295 return write!(f, "0s");
296 }
297
298 let years = self.months / 12;
299 let months = self.months % 12;
300
301 let total_seconds = self.nanos / 1_000_000_000;
302 let remaining_nanos = self.nanos % 1_000_000_000;
303
304 let extra_days = total_seconds / 86400;
305 let remaining_seconds = total_seconds % 86400;
306
307 let display_days = self.days + extra_days as i32;
308 let hours = remaining_seconds / 3600;
309 let minutes = (remaining_seconds % 3600) / 60;
310 let seconds = remaining_seconds % 60;
311
312 let abs_remaining = remaining_nanos.abs();
313 let ms = abs_remaining / 1_000_000;
314 let us = (abs_remaining % 1_000_000) / 1_000;
315 let ns = abs_remaining % 1_000;
316
317 if years != 0 {
318 write!(f, "{}y", years)?;
319 }
320 if months != 0 {
321 write!(f, "{}mo", months)?;
322 }
323 if display_days != 0 {
324 write!(f, "{}d", display_days)?;
325 }
326 if hours != 0 {
327 write!(f, "{}h", hours)?;
328 }
329 if minutes != 0 {
330 write!(f, "{}m", minutes)?;
331 }
332 if seconds != 0 {
333 write!(f, "{}s", seconds)?;
334 }
335
336 if ms != 0 || us != 0 || ns != 0 {
337 if remaining_nanos < 0
338 && seconds == 0 && hours == 0
339 && minutes == 0 && display_days == 0
340 && years == 0 && months == 0
341 {
342 write!(f, "-")?;
343 }
344 if ms != 0 {
345 write!(f, "{}ms", ms)?;
346 }
347 if us != 0 {
348 write!(f, "{}us", us)?;
349 }
350 if ns != 0 {
351 write!(f, "{}ns", ns)?;
352 }
353 }
354
355 Ok(())
356 }
357}
358
359#[cfg(test)]
360pub mod tests {
361 use super::*;
362
363 #[test]
366 fn test_duration_iso_string_zero() {
367 assert_eq!(Duration::zero().to_iso_string(), "PT0S");
368 assert_eq!(Duration::from_seconds(0).to_iso_string(), "PT0S");
369 assert_eq!(Duration::from_nanoseconds(0).to_iso_string(), "PT0S");
370 assert_eq!(Duration::default().to_iso_string(), "PT0S");
371 }
372
373 #[test]
374 fn test_duration_iso_string_seconds() {
375 assert_eq!(Duration::from_seconds(1).to_iso_string(), "PT1S");
376 assert_eq!(Duration::from_seconds(30).to_iso_string(), "PT30S");
377 assert_eq!(Duration::from_seconds(59).to_iso_string(), "PT59S");
378 }
379
380 #[test]
381 fn test_duration_iso_string_minutes() {
382 assert_eq!(Duration::from_minutes(1).to_iso_string(), "PT1M");
383 assert_eq!(Duration::from_minutes(30).to_iso_string(), "PT30M");
384 assert_eq!(Duration::from_minutes(59).to_iso_string(), "PT59M");
385 }
386
387 #[test]
388 fn test_duration_iso_string_hours() {
389 assert_eq!(Duration::from_hours(1).to_iso_string(), "PT1H");
390 assert_eq!(Duration::from_hours(12).to_iso_string(), "PT12H");
391 assert_eq!(Duration::from_hours(23).to_iso_string(), "PT23H");
392 }
393
394 #[test]
395 fn test_duration_iso_string_days() {
396 assert_eq!(Duration::from_days(1).to_iso_string(), "P1D");
397 assert_eq!(Duration::from_days(7).to_iso_string(), "P7D");
398 assert_eq!(Duration::from_days(365).to_iso_string(), "P365D");
399 }
400
401 #[test]
402 fn test_duration_iso_string_weeks() {
403 assert_eq!(Duration::from_weeks(1).to_iso_string(), "P7D");
404 assert_eq!(Duration::from_weeks(2).to_iso_string(), "P14D");
405 assert_eq!(Duration::from_weeks(52).to_iso_string(), "P364D");
406 }
407
408 #[test]
409 fn test_duration_iso_string_combined_time() {
410 let d = Duration::new(0, 0, (1 * 60 * 60 + 30 * 60) * 1_000_000_000);
411 assert_eq!(d.to_iso_string(), "PT1H30M");
412
413 let d = Duration::new(0, 0, (5 * 60 + 45) * 1_000_000_000);
414 assert_eq!(d.to_iso_string(), "PT5M45S");
415
416 let d = Duration::new(0, 0, (2 * 60 * 60 + 30 * 60 + 45) * 1_000_000_000);
417 assert_eq!(d.to_iso_string(), "PT2H30M45S");
418 }
419
420 #[test]
421 fn test_duration_iso_string_combined_date_time() {
422 assert_eq!(Duration::new(0, 1, 2 * 60 * 60 * 1_000_000_000).to_iso_string(), "P1DT2H");
423 assert_eq!(Duration::new(0, 1, 30 * 60 * 1_000_000_000).to_iso_string(), "P1DT30M");
424 assert_eq!(Duration::new(0, 1, (2 * 60 * 60 + 30 * 60) * 1_000_000_000).to_iso_string(), "P1DT2H30M");
425 assert_eq!(
426 Duration::new(0, 1, (2 * 60 * 60 + 30 * 60 + 45) * 1_000_000_000).to_iso_string(),
427 "P1DT2H30M45S"
428 );
429 }
430
431 #[test]
432 fn test_duration_iso_string_milliseconds() {
433 assert_eq!(Duration::from_milliseconds(123).to_iso_string(), "PT0.123S");
434 assert_eq!(Duration::from_milliseconds(1).to_iso_string(), "PT0.001S");
435 assert_eq!(Duration::from_milliseconds(999).to_iso_string(), "PT0.999S");
436 assert_eq!(Duration::from_milliseconds(1500).to_iso_string(), "PT1.5S");
437 }
438
439 #[test]
440 fn test_duration_iso_string_microseconds() {
441 assert_eq!(Duration::from_microseconds(123456).to_iso_string(), "PT0.123456S");
442 assert_eq!(Duration::from_microseconds(1).to_iso_string(), "PT0.000001S");
443 assert_eq!(Duration::from_microseconds(999999).to_iso_string(), "PT0.999999S");
444 assert_eq!(Duration::from_microseconds(1500000).to_iso_string(), "PT1.5S");
445 }
446
447 #[test]
448 fn test_duration_iso_string_nanoseconds() {
449 assert_eq!(Duration::from_nanoseconds(123456789).to_iso_string(), "PT0.123456789S");
450 assert_eq!(Duration::from_nanoseconds(1).to_iso_string(), "PT0.000000001S");
451 assert_eq!(Duration::from_nanoseconds(999999999).to_iso_string(), "PT0.999999999S");
452 assert_eq!(Duration::from_nanoseconds(1500000000).to_iso_string(), "PT1.5S");
453 }
454
455 #[test]
456 fn test_duration_iso_string_fractional_seconds() {
457 let d = Duration::new(0, 0, 1 * 1_000_000_000 + 500 * 1_000_000);
458 assert_eq!(d.to_iso_string(), "PT1.5S");
459
460 let d = Duration::new(0, 0, 2 * 1_000_000_000 + 123456 * 1_000);
461 assert_eq!(d.to_iso_string(), "PT2.123456S");
462
463 let d = Duration::new(0, 0, 3 * 1_000_000_000 + 123456789);
464 assert_eq!(d.to_iso_string(), "PT3.123456789S");
465 }
466
467 #[test]
468 fn test_duration_iso_string_complex() {
469 let d = Duration::new(0, 1, (2 * 60 * 60 + 30 * 60 + 45) * 1_000_000_000 + 123 * 1_000_000);
470 assert_eq!(d.to_iso_string(), "P1DT2H30M45.123S");
471
472 let d = Duration::new(0, 7, (12 * 60 * 60 + 45 * 60 + 30) * 1_000_000_000 + 456789 * 1_000);
473 assert_eq!(d.to_iso_string(), "P7DT12H45M30.456789S");
474 }
475
476 #[test]
477 fn test_duration_iso_string_trailing_zeros() {
478 assert_eq!(Duration::from_nanoseconds(100000000).to_iso_string(), "PT0.1S");
479 assert_eq!(Duration::from_nanoseconds(120000000).to_iso_string(), "PT0.12S");
480 assert_eq!(Duration::from_nanoseconds(123000000).to_iso_string(), "PT0.123S");
481 assert_eq!(Duration::from_nanoseconds(123400000).to_iso_string(), "PT0.1234S");
482 assert_eq!(Duration::from_nanoseconds(123450000).to_iso_string(), "PT0.12345S");
483 assert_eq!(Duration::from_nanoseconds(123456000).to_iso_string(), "PT0.123456S");
484 assert_eq!(Duration::from_nanoseconds(123456700).to_iso_string(), "PT0.1234567S");
485 assert_eq!(Duration::from_nanoseconds(123456780).to_iso_string(), "PT0.12345678S");
486 assert_eq!(Duration::from_nanoseconds(123456789).to_iso_string(), "PT0.123456789S");
487 }
488
489 #[test]
490 fn test_duration_iso_string_negative() {
491 assert_eq!(Duration::from_seconds(-30).to_iso_string(), "PT-30S");
492 assert_eq!(Duration::from_minutes(-5).to_iso_string(), "PT-5M");
493 assert_eq!(Duration::from_hours(-2).to_iso_string(), "PT-2H");
494 assert_eq!(Duration::from_days(-1).to_iso_string(), "P-1D");
495 }
496
497 #[test]
498 fn test_duration_iso_string_large() {
499 assert_eq!(Duration::from_days(1000).to_iso_string(), "P1000D");
500 assert_eq!(Duration::from_hours(25).to_iso_string(), "P1DT1H");
501 assert_eq!(Duration::from_minutes(1500).to_iso_string(), "P1DT1H");
502 assert_eq!(Duration::from_seconds(90000).to_iso_string(), "P1DT1H");
503 }
504
505 #[test]
506 fn test_duration_iso_string_edge_cases() {
507 assert_eq!(Duration::from_nanoseconds(1).to_iso_string(), "PT0.000000001S");
508 assert_eq!(Duration::from_nanoseconds(999999999).to_iso_string(), "PT0.999999999S");
509 assert_eq!(Duration::from_nanoseconds(1000000000).to_iso_string(), "PT1S");
510 assert_eq!(Duration::from_nanoseconds(60 * 1000000000).to_iso_string(), "PT1M");
511 assert_eq!(Duration::from_nanoseconds(3600 * 1000000000).to_iso_string(), "PT1H");
512 assert_eq!(Duration::from_nanoseconds(86400 * 1000000000).to_iso_string(), "P1D");
513 }
514
515 #[test]
516 fn test_duration_iso_string_precision() {
517 assert_eq!(Duration::from_nanoseconds(100).to_iso_string(), "PT0.0000001S");
518 assert_eq!(Duration::from_nanoseconds(10).to_iso_string(), "PT0.00000001S");
519 assert_eq!(Duration::from_nanoseconds(1).to_iso_string(), "PT0.000000001S");
520 }
521
522 #[test]
525 fn test_duration_display_zero() {
526 assert_eq!(format!("{}", Duration::zero()), "0s");
527 assert_eq!(format!("{}", Duration::from_seconds(0)), "0s");
528 assert_eq!(format!("{}", Duration::from_nanoseconds(0)), "0s");
529 assert_eq!(format!("{}", Duration::default()), "0s");
530 }
531
532 #[test]
533 fn test_duration_display_seconds_only() {
534 assert_eq!(format!("{}", Duration::from_seconds(1)), "1s");
535 assert_eq!(format!("{}", Duration::from_seconds(30)), "30s");
536 assert_eq!(format!("{}", Duration::from_seconds(59)), "59s");
537 }
538
539 #[test]
540 fn test_duration_display_minutes_only() {
541 assert_eq!(format!("{}", Duration::from_minutes(1)), "1m");
542 assert_eq!(format!("{}", Duration::from_minutes(30)), "30m");
543 assert_eq!(format!("{}", Duration::from_minutes(59)), "59m");
544 }
545
546 #[test]
547 fn test_duration_display_hours_only() {
548 assert_eq!(format!("{}", Duration::from_hours(1)), "1h");
549 assert_eq!(format!("{}", Duration::from_hours(12)), "12h");
550 assert_eq!(format!("{}", Duration::from_hours(23)), "23h");
551 }
552
553 #[test]
554 fn test_duration_display_days_only() {
555 assert_eq!(format!("{}", Duration::from_days(1)), "1d");
556 assert_eq!(format!("{}", Duration::from_days(7)), "7d");
557 assert_eq!(format!("{}", Duration::from_days(365)), "365d");
558 }
559
560 #[test]
561 fn test_duration_display_weeks_only() {
562 assert_eq!(format!("{}", Duration::from_weeks(1)), "7d");
563 assert_eq!(format!("{}", Duration::from_weeks(2)), "14d");
564 assert_eq!(format!("{}", Duration::from_weeks(52)), "364d");
565 }
566
567 #[test]
568 fn test_duration_display_months_only() {
569 assert_eq!(format!("{}", Duration::from_months(1)), "1mo");
570 assert_eq!(format!("{}", Duration::from_months(6)), "6mo");
571 assert_eq!(format!("{}", Duration::from_months(11)), "11mo");
572 }
573
574 #[test]
575 fn test_duration_display_years_only() {
576 assert_eq!(format!("{}", Duration::from_years(1)), "1y");
577 assert_eq!(format!("{}", Duration::from_years(10)), "10y");
578 assert_eq!(format!("{}", Duration::from_years(100)), "100y");
579 }
580
581 #[test]
582 fn test_duration_display_combined_time() {
583 let d = Duration::new(0, 0, (1 * 60 * 60 + 30 * 60) * 1_000_000_000);
584 assert_eq!(format!("{}", d), "1h30m");
585
586 let d = Duration::new(0, 0, (5 * 60 + 45) * 1_000_000_000);
587 assert_eq!(format!("{}", d), "5m45s");
588
589 let d = Duration::new(0, 0, (2 * 60 * 60 + 30 * 60 + 45) * 1_000_000_000);
590 assert_eq!(format!("{}", d), "2h30m45s");
591 }
592
593 #[test]
594 fn test_duration_display_combined_date_time() {
595 assert_eq!(format!("{}", Duration::new(0, 1, 2 * 60 * 60 * 1_000_000_000)), "1d2h");
596 assert_eq!(format!("{}", Duration::new(0, 1, 30 * 60 * 1_000_000_000)), "1d30m");
597 assert_eq!(format!("{}", Duration::new(0, 1, (2 * 60 * 60 + 30 * 60) * 1_000_000_000)), "1d2h30m");
598 assert_eq!(
599 format!("{}", Duration::new(0, 1, (2 * 60 * 60 + 30 * 60 + 45) * 1_000_000_000)),
600 "1d2h30m45s"
601 );
602 }
603
604 #[test]
605 fn test_duration_display_years_months() {
606 assert_eq!(format!("{}", Duration::new(13, 0, 0)), "1y1mo");
607 assert_eq!(format!("{}", Duration::new(27, 0, 0)), "2y3mo");
608 }
609
610 #[test]
611 fn test_duration_display_full_components() {
612 let nanos = (4 * 60 * 60 + 5 * 60 + 6) * 1_000_000_000i64;
613 assert_eq!(format!("{}", Duration::new(14, 3, nanos)), "1y2mo3d4h5m6s");
614 }
615
616 #[test]
617 fn test_duration_display_milliseconds() {
618 assert_eq!(format!("{}", Duration::from_milliseconds(123)), "123ms");
619 assert_eq!(format!("{}", Duration::from_milliseconds(1)), "1ms");
620 assert_eq!(format!("{}", Duration::from_milliseconds(999)), "999ms");
621 assert_eq!(format!("{}", Duration::from_milliseconds(1500)), "1s500ms");
622 }
623
624 #[test]
625 fn test_duration_display_microseconds() {
626 assert_eq!(format!("{}", Duration::from_microseconds(123456)), "123ms456us");
627 assert_eq!(format!("{}", Duration::from_microseconds(1)), "1us");
628 assert_eq!(format!("{}", Duration::from_microseconds(999999)), "999ms999us");
629 assert_eq!(format!("{}", Duration::from_microseconds(1500000)), "1s500ms");
630 }
631
632 #[test]
633 fn test_duration_display_nanoseconds() {
634 assert_eq!(format!("{}", Duration::from_nanoseconds(123456789)), "123ms456us789ns");
635 assert_eq!(format!("{}", Duration::from_nanoseconds(1)), "1ns");
636 assert_eq!(format!("{}", Duration::from_nanoseconds(999999999)), "999ms999us999ns");
637 assert_eq!(format!("{}", Duration::from_nanoseconds(1500000000)), "1s500ms");
638 }
639
640 #[test]
641 fn test_duration_display_sub_second_decomposition() {
642 let d = Duration::new(0, 0, 1 * 1_000_000_000 + 500 * 1_000_000);
643 assert_eq!(format!("{}", d), "1s500ms");
644
645 let d = Duration::new(0, 0, 2 * 1_000_000_000 + 123456 * 1_000);
646 assert_eq!(format!("{}", d), "2s123ms456us");
647
648 let d = Duration::new(0, 0, 3 * 1_000_000_000 + 123456789);
649 assert_eq!(format!("{}", d), "3s123ms456us789ns");
650 }
651
652 #[test]
653 fn test_duration_display_complex() {
654 let d = Duration::new(0, 1, (2 * 60 * 60 + 30 * 60 + 45) * 1_000_000_000 + 123 * 1_000_000);
655 assert_eq!(format!("{}", d), "1d2h30m45s123ms");
656
657 let d = Duration::new(0, 7, (12 * 60 * 60 + 45 * 60 + 30) * 1_000_000_000 + 456789 * 1_000);
658 assert_eq!(format!("{}", d), "7d12h45m30s456ms789us");
659 }
660
661 #[test]
662 fn test_duration_display_sub_second_only() {
663 assert_eq!(format!("{}", Duration::from_nanoseconds(100000000)), "100ms");
664 assert_eq!(format!("{}", Duration::from_nanoseconds(120000000)), "120ms");
665 assert_eq!(format!("{}", Duration::from_nanoseconds(123000000)), "123ms");
666 assert_eq!(format!("{}", Duration::from_nanoseconds(100)), "100ns");
667 assert_eq!(format!("{}", Duration::from_nanoseconds(10)), "10ns");
668 assert_eq!(format!("{}", Duration::from_nanoseconds(1000)), "1us");
669 }
670
671 #[test]
672 fn test_duration_display_negative() {
673 assert_eq!(format!("{}", Duration::from_seconds(-30)), "-30s");
674 assert_eq!(format!("{}", Duration::from_minutes(-5)), "-5m");
675 assert_eq!(format!("{}", Duration::from_hours(-2)), "-2h");
676 assert_eq!(format!("{}", Duration::from_days(-1)), "-1d");
677 }
678
679 #[test]
680 fn test_duration_display_negative_sub_second() {
681 assert_eq!(format!("{}", Duration::from_milliseconds(-500)), "-500ms");
682 assert_eq!(format!("{}", Duration::from_microseconds(-100)), "-100us");
683 assert_eq!(format!("{}", Duration::from_nanoseconds(-50)), "-50ns");
684 }
685
686 #[test]
687 fn test_duration_display_large() {
688 assert_eq!(format!("{}", Duration::from_days(1000)), "1000d");
689 assert_eq!(format!("{}", Duration::from_hours(25)), "1d1h");
690 assert_eq!(format!("{}", Duration::from_minutes(1500)), "1d1h");
691 assert_eq!(format!("{}", Duration::from_seconds(90000)), "1d1h");
692 }
693
694 #[test]
695 fn test_duration_display_edge_cases() {
696 assert_eq!(format!("{}", Duration::from_nanoseconds(1)), "1ns");
697 assert_eq!(format!("{}", Duration::from_nanoseconds(999999999)), "999ms999us999ns");
698 assert_eq!(format!("{}", Duration::from_nanoseconds(1000000000)), "1s");
699 assert_eq!(format!("{}", Duration::from_nanoseconds(60 * 1000000000)), "1m");
700 assert_eq!(format!("{}", Duration::from_nanoseconds(3600 * 1000000000)), "1h");
701 assert_eq!(format!("{}", Duration::from_nanoseconds(86400 * 1000000000)), "1d");
702 }
703
704 #[test]
705 fn test_duration_display_abs_and_negate() {
706 let d = Duration::from_seconds(-30);
707 assert_eq!(format!("{}", d.abs()), "30s");
708
709 let d = Duration::from_seconds(30);
710 assert_eq!(format!("{}", d.negate()), "-30s");
711 }
712}