1use crate::{DateTime, NaiveDateTime, TimeDelta, TimeZone, Timelike};
7use core::cmp::Ordering;
8use core::fmt;
9use core::marker::Sized;
10use core::ops::{Add, Sub};
11
12pub trait SubsecRound {
19 fn round_subsecs(self, digits: u16) -> Self;
31
32 fn trunc_subsecs(self, digits: u16) -> Self;
43}
44
45impl<T> SubsecRound for T
46where
47 T: Timelike + Add<TimeDelta, Output = T> + Sub<TimeDelta, Output = T>,
48{
49 fn round_subsecs(self, digits: u16) -> T {
50 let span = span_for_digits(digits);
51 let delta_down = self.nanosecond() % span;
52 if delta_down > 0 {
53 let delta_up = span - delta_down;
54 if delta_up <= delta_down {
55 self + TimeDelta::nanoseconds(delta_up.into())
56 } else {
57 self - TimeDelta::nanoseconds(delta_down.into())
58 }
59 } else {
60 self }
62 }
63
64 fn trunc_subsecs(self, digits: u16) -> T {
65 let span = span_for_digits(digits);
66 let delta_down = self.nanosecond() % span;
67 if delta_down > 0 {
68 self - TimeDelta::nanoseconds(delta_down.into())
69 } else {
70 self }
72 }
73}
74
75const fn span_for_digits(digits: u16) -> u32 {
77 match digits {
79 0 => 1_000_000_000,
80 1 => 100_000_000,
81 2 => 10_000_000,
82 3 => 1_000_000,
83 4 => 100_000,
84 5 => 10_000,
85 6 => 1_000,
86 7 => 100,
87 8 => 10,
88 _ => 1,
89 }
90}
91
92pub trait DurationRound: Sized {
100 #[cfg(feature = "std")]
102 type Err: std::error::Error;
103
104 #[cfg(not(feature = "std"))]
106 type Err: fmt::Debug + fmt::Display;
107
108 fn duration_round(self, duration: TimeDelta) -> Result<Self, Self::Err>;
124
125 fn duration_trunc(self, duration: TimeDelta) -> Result<Self, Self::Err>;
141}
142
143impl<Tz: TimeZone> DurationRound for DateTime<Tz> {
144 type Err = RoundingError;
145
146 fn duration_round(self, duration: TimeDelta) -> Result<Self, Self::Err> {
147 duration_round(self.naive_local(), self, duration)
148 }
149
150 fn duration_trunc(self, duration: TimeDelta) -> Result<Self, Self::Err> {
151 duration_trunc(self.naive_local(), self, duration)
152 }
153}
154
155impl DurationRound for NaiveDateTime {
156 type Err = RoundingError;
157
158 fn duration_round(self, duration: TimeDelta) -> Result<Self, Self::Err> {
159 duration_round(self, self, duration)
160 }
161
162 fn duration_trunc(self, duration: TimeDelta) -> Result<Self, Self::Err> {
163 duration_trunc(self, self, duration)
164 }
165}
166
167fn duration_round<T>(
168 naive: NaiveDateTime,
169 original: T,
170 duration: TimeDelta,
171) -> Result<T, RoundingError>
172where
173 T: Timelike + Add<TimeDelta, Output = T> + Sub<TimeDelta, Output = T>,
174{
175 if let Some(span) = duration.num_nanoseconds() {
176 if span < 0 {
177 return Err(RoundingError::DurationExceedsLimit);
178 }
179 let stamp = naive.timestamp_nanos_opt().ok_or(RoundingError::TimestampExceedsLimit)?;
180 if span == 0 {
181 return Ok(original);
182 }
183 let delta_down = stamp % span;
184 if delta_down == 0 {
185 Ok(original)
186 } else {
187 let (delta_up, delta_down) = if delta_down < 0 {
188 (delta_down.abs(), span - delta_down.abs())
189 } else {
190 (span - delta_down, delta_down)
191 };
192 if delta_up <= delta_down {
193 Ok(original + TimeDelta::nanoseconds(delta_up))
194 } else {
195 Ok(original - TimeDelta::nanoseconds(delta_down))
196 }
197 }
198 } else {
199 Err(RoundingError::DurationExceedsLimit)
200 }
201}
202
203fn duration_trunc<T>(
204 naive: NaiveDateTime,
205 original: T,
206 duration: TimeDelta,
207) -> Result<T, RoundingError>
208where
209 T: Timelike + Add<TimeDelta, Output = T> + Sub<TimeDelta, Output = T>,
210{
211 if let Some(span) = duration.num_nanoseconds() {
212 if span < 0 {
213 return Err(RoundingError::DurationExceedsLimit);
214 }
215 let stamp = naive.timestamp_nanos_opt().ok_or(RoundingError::TimestampExceedsLimit)?;
216 let delta_down = stamp % span;
217 match delta_down.cmp(&0) {
218 Ordering::Equal => Ok(original),
219 Ordering::Greater => Ok(original - TimeDelta::nanoseconds(delta_down)),
220 Ordering::Less => Ok(original - TimeDelta::nanoseconds(span - delta_down.abs())),
221 }
222 } else {
223 Err(RoundingError::DurationExceedsLimit)
224 }
225}
226
227#[derive(Debug, Clone, PartialEq, Eq, Copy)]
231pub enum RoundingError {
232 DurationExceedsTimestamp,
236
237 DurationExceedsLimit,
249
250 TimestampExceedsLimit,
259}
260
261impl fmt::Display for RoundingError {
262 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
263 match *self {
264 RoundingError::DurationExceedsTimestamp => {
265 write!(f, "duration in nanoseconds exceeds timestamp")
266 }
267 RoundingError::DurationExceedsLimit => {
268 write!(f, "duration exceeds num_nanoseconds limit")
269 }
270 RoundingError::TimestampExceedsLimit => {
271 write!(f, "timestamp exceeds num_nanoseconds limit")
272 }
273 }
274 }
275}
276
277#[cfg(feature = "std")]
278impl std::error::Error for RoundingError {
279 #[allow(deprecated)]
280 fn description(&self) -> &str {
281 "error from rounding or truncating with DurationRound"
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use super::{DurationRound, RoundingError, SubsecRound, TimeDelta};
288 use crate::offset::{FixedOffset, TimeZone, Utc};
289 use crate::Timelike;
290 use crate::{NaiveDate, NaiveDateTime};
291
292 #[test]
293 fn test_round_subsecs() {
294 let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap();
295 let dt = pst
296 .from_local_datetime(
297 &NaiveDate::from_ymd_opt(2018, 1, 11)
298 .unwrap()
299 .and_hms_nano_opt(10, 5, 13, 84_660_684)
300 .unwrap(),
301 )
302 .unwrap();
303
304 assert_eq!(dt.round_subsecs(10), dt);
305 assert_eq!(dt.round_subsecs(9), dt);
306 assert_eq!(dt.round_subsecs(8).nanosecond(), 84_660_680);
307 assert_eq!(dt.round_subsecs(7).nanosecond(), 84_660_700);
308 assert_eq!(dt.round_subsecs(6).nanosecond(), 84_661_000);
309 assert_eq!(dt.round_subsecs(5).nanosecond(), 84_660_000);
310 assert_eq!(dt.round_subsecs(4).nanosecond(), 84_700_000);
311 assert_eq!(dt.round_subsecs(3).nanosecond(), 85_000_000);
312 assert_eq!(dt.round_subsecs(2).nanosecond(), 80_000_000);
313 assert_eq!(dt.round_subsecs(1).nanosecond(), 100_000_000);
314
315 assert_eq!(dt.round_subsecs(0).nanosecond(), 0);
316 assert_eq!(dt.round_subsecs(0).second(), 13);
317
318 let dt = Utc
319 .from_local_datetime(
320 &NaiveDate::from_ymd_opt(2018, 1, 11)
321 .unwrap()
322 .and_hms_nano_opt(10, 5, 27, 750_500_000)
323 .unwrap(),
324 )
325 .unwrap();
326 assert_eq!(dt.round_subsecs(9), dt);
327 assert_eq!(dt.round_subsecs(4), dt);
328 assert_eq!(dt.round_subsecs(3).nanosecond(), 751_000_000);
329 assert_eq!(dt.round_subsecs(2).nanosecond(), 750_000_000);
330 assert_eq!(dt.round_subsecs(1).nanosecond(), 800_000_000);
331
332 assert_eq!(dt.round_subsecs(0).nanosecond(), 0);
333 assert_eq!(dt.round_subsecs(0).second(), 28);
334 }
335
336 #[test]
337 fn test_round_leap_nanos() {
338 let dt = Utc
339 .from_local_datetime(
340 &NaiveDate::from_ymd_opt(2016, 12, 31)
341 .unwrap()
342 .and_hms_nano_opt(23, 59, 59, 1_750_500_000)
343 .unwrap(),
344 )
345 .unwrap();
346 assert_eq!(dt.round_subsecs(9), dt);
347 assert_eq!(dt.round_subsecs(4), dt);
348 assert_eq!(dt.round_subsecs(2).nanosecond(), 1_750_000_000);
349 assert_eq!(dt.round_subsecs(1).nanosecond(), 1_800_000_000);
350 assert_eq!(dt.round_subsecs(1).second(), 59);
351
352 assert_eq!(dt.round_subsecs(0).nanosecond(), 0);
353 assert_eq!(dt.round_subsecs(0).second(), 0);
354 }
355
356 #[test]
357 fn test_trunc_subsecs() {
358 let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap();
359 let dt = pst
360 .from_local_datetime(
361 &NaiveDate::from_ymd_opt(2018, 1, 11)
362 .unwrap()
363 .and_hms_nano_opt(10, 5, 13, 84_660_684)
364 .unwrap(),
365 )
366 .unwrap();
367
368 assert_eq!(dt.trunc_subsecs(10), dt);
369 assert_eq!(dt.trunc_subsecs(9), dt);
370 assert_eq!(dt.trunc_subsecs(8).nanosecond(), 84_660_680);
371 assert_eq!(dt.trunc_subsecs(7).nanosecond(), 84_660_600);
372 assert_eq!(dt.trunc_subsecs(6).nanosecond(), 84_660_000);
373 assert_eq!(dt.trunc_subsecs(5).nanosecond(), 84_660_000);
374 assert_eq!(dt.trunc_subsecs(4).nanosecond(), 84_600_000);
375 assert_eq!(dt.trunc_subsecs(3).nanosecond(), 84_000_000);
376 assert_eq!(dt.trunc_subsecs(2).nanosecond(), 80_000_000);
377 assert_eq!(dt.trunc_subsecs(1).nanosecond(), 0);
378
379 assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0);
380 assert_eq!(dt.trunc_subsecs(0).second(), 13);
381
382 let dt = pst
383 .from_local_datetime(
384 &NaiveDate::from_ymd_opt(2018, 1, 11)
385 .unwrap()
386 .and_hms_nano_opt(10, 5, 27, 750_500_000)
387 .unwrap(),
388 )
389 .unwrap();
390 assert_eq!(dt.trunc_subsecs(9), dt);
391 assert_eq!(dt.trunc_subsecs(4), dt);
392 assert_eq!(dt.trunc_subsecs(3).nanosecond(), 750_000_000);
393 assert_eq!(dt.trunc_subsecs(2).nanosecond(), 750_000_000);
394 assert_eq!(dt.trunc_subsecs(1).nanosecond(), 700_000_000);
395
396 assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0);
397 assert_eq!(dt.trunc_subsecs(0).second(), 27);
398 }
399
400 #[test]
401 fn test_trunc_leap_nanos() {
402 let dt = Utc
403 .from_local_datetime(
404 &NaiveDate::from_ymd_opt(2016, 12, 31)
405 .unwrap()
406 .and_hms_nano_opt(23, 59, 59, 1_750_500_000)
407 .unwrap(),
408 )
409 .unwrap();
410 assert_eq!(dt.trunc_subsecs(9), dt);
411 assert_eq!(dt.trunc_subsecs(4), dt);
412 assert_eq!(dt.trunc_subsecs(2).nanosecond(), 1_750_000_000);
413 assert_eq!(dt.trunc_subsecs(1).nanosecond(), 1_700_000_000);
414 assert_eq!(dt.trunc_subsecs(1).second(), 59);
415
416 assert_eq!(dt.trunc_subsecs(0).nanosecond(), 1_000_000_000);
417 assert_eq!(dt.trunc_subsecs(0).second(), 59);
418 }
419
420 #[test]
421 fn test_duration_round() {
422 let dt = Utc
423 .from_local_datetime(
424 &NaiveDate::from_ymd_opt(2016, 12, 31)
425 .unwrap()
426 .and_hms_nano_opt(23, 59, 59, 175_500_000)
427 .unwrap(),
428 )
429 .unwrap();
430
431 assert_eq!(
432 dt.duration_round(TimeDelta::zero()).unwrap().to_string(),
433 "2016-12-31 23:59:59.175500 UTC"
434 );
435
436 assert_eq!(
437 dt.duration_round(TimeDelta::milliseconds(10)).unwrap().to_string(),
438 "2016-12-31 23:59:59.180 UTC"
439 );
440
441 let dt = Utc
443 .from_local_datetime(
444 &NaiveDate::from_ymd_opt(2012, 12, 12)
445 .unwrap()
446 .and_hms_milli_opt(18, 22, 30, 0)
447 .unwrap(),
448 )
449 .unwrap();
450 assert_eq!(
451 dt.duration_round(TimeDelta::minutes(5)).unwrap().to_string(),
452 "2012-12-12 18:25:00 UTC"
453 );
454 let dt = Utc
456 .from_local_datetime(
457 &NaiveDate::from_ymd_opt(2012, 12, 12)
458 .unwrap()
459 .and_hms_milli_opt(18, 22, 29, 999)
460 .unwrap(),
461 )
462 .unwrap();
463 assert_eq!(
464 dt.duration_round(TimeDelta::minutes(5)).unwrap().to_string(),
465 "2012-12-12 18:20:00 UTC"
466 );
467
468 assert_eq!(
469 dt.duration_round(TimeDelta::minutes(10)).unwrap().to_string(),
470 "2012-12-12 18:20:00 UTC"
471 );
472 assert_eq!(
473 dt.duration_round(TimeDelta::minutes(30)).unwrap().to_string(),
474 "2012-12-12 18:30:00 UTC"
475 );
476 assert_eq!(
477 dt.duration_round(TimeDelta::hours(1)).unwrap().to_string(),
478 "2012-12-12 18:00:00 UTC"
479 );
480 assert_eq!(
481 dt.duration_round(TimeDelta::days(1)).unwrap().to_string(),
482 "2012-12-13 00:00:00 UTC"
483 );
484
485 let dt =
487 FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap();
488 assert_eq!(
489 dt.duration_round(TimeDelta::days(1)).unwrap().to_string(),
490 "2020-10-28 00:00:00 +01:00"
491 );
492 assert_eq!(
493 dt.duration_round(TimeDelta::weeks(1)).unwrap().to_string(),
494 "2020-10-29 00:00:00 +01:00"
495 );
496
497 let dt =
499 FixedOffset::west_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap();
500 assert_eq!(
501 dt.duration_round(TimeDelta::days(1)).unwrap().to_string(),
502 "2020-10-28 00:00:00 -01:00"
503 );
504 assert_eq!(
505 dt.duration_round(TimeDelta::weeks(1)).unwrap().to_string(),
506 "2020-10-29 00:00:00 -01:00"
507 );
508 }
509
510 #[test]
511 fn test_duration_round_naive() {
512 let dt = Utc
513 .from_local_datetime(
514 &NaiveDate::from_ymd_opt(2016, 12, 31)
515 .unwrap()
516 .and_hms_nano_opt(23, 59, 59, 175_500_000)
517 .unwrap(),
518 )
519 .unwrap()
520 .naive_utc();
521
522 assert_eq!(
523 dt.duration_round(TimeDelta::zero()).unwrap().to_string(),
524 "2016-12-31 23:59:59.175500"
525 );
526
527 assert_eq!(
528 dt.duration_round(TimeDelta::milliseconds(10)).unwrap().to_string(),
529 "2016-12-31 23:59:59.180"
530 );
531
532 let dt = Utc
534 .from_local_datetime(
535 &NaiveDate::from_ymd_opt(2012, 12, 12)
536 .unwrap()
537 .and_hms_milli_opt(18, 22, 30, 0)
538 .unwrap(),
539 )
540 .unwrap()
541 .naive_utc();
542 assert_eq!(
543 dt.duration_round(TimeDelta::minutes(5)).unwrap().to_string(),
544 "2012-12-12 18:25:00"
545 );
546 let dt = Utc
548 .from_local_datetime(
549 &NaiveDate::from_ymd_opt(2012, 12, 12)
550 .unwrap()
551 .and_hms_milli_opt(18, 22, 29, 999)
552 .unwrap(),
553 )
554 .unwrap()
555 .naive_utc();
556 assert_eq!(
557 dt.duration_round(TimeDelta::minutes(5)).unwrap().to_string(),
558 "2012-12-12 18:20:00"
559 );
560
561 assert_eq!(
562 dt.duration_round(TimeDelta::minutes(10)).unwrap().to_string(),
563 "2012-12-12 18:20:00"
564 );
565 assert_eq!(
566 dt.duration_round(TimeDelta::minutes(30)).unwrap().to_string(),
567 "2012-12-12 18:30:00"
568 );
569 assert_eq!(
570 dt.duration_round(TimeDelta::hours(1)).unwrap().to_string(),
571 "2012-12-12 18:00:00"
572 );
573 assert_eq!(
574 dt.duration_round(TimeDelta::days(1)).unwrap().to_string(),
575 "2012-12-13 00:00:00"
576 );
577 }
578
579 #[test]
580 fn test_duration_round_pre_epoch() {
581 let dt = Utc.with_ymd_and_hms(1969, 12, 12, 12, 12, 12).unwrap();
582 assert_eq!(
583 dt.duration_round(TimeDelta::minutes(10)).unwrap().to_string(),
584 "1969-12-12 12:10:00 UTC"
585 );
586 }
587
588 #[test]
589 fn test_duration_trunc() {
590 let dt = Utc
591 .from_local_datetime(
592 &NaiveDate::from_ymd_opt(2016, 12, 31)
593 .unwrap()
594 .and_hms_nano_opt(23, 59, 59, 175_500_000)
595 .unwrap(),
596 )
597 .unwrap();
598
599 assert_eq!(
600 dt.duration_trunc(TimeDelta::milliseconds(10)).unwrap().to_string(),
601 "2016-12-31 23:59:59.170 UTC"
602 );
603
604 let dt = Utc
606 .from_local_datetime(
607 &NaiveDate::from_ymd_opt(2012, 12, 12)
608 .unwrap()
609 .and_hms_milli_opt(18, 22, 30, 0)
610 .unwrap(),
611 )
612 .unwrap();
613 assert_eq!(
614 dt.duration_trunc(TimeDelta::minutes(5)).unwrap().to_string(),
615 "2012-12-12 18:20:00 UTC"
616 );
617 let dt = Utc
619 .from_local_datetime(
620 &NaiveDate::from_ymd_opt(2012, 12, 12)
621 .unwrap()
622 .and_hms_milli_opt(18, 22, 29, 999)
623 .unwrap(),
624 )
625 .unwrap();
626 assert_eq!(
627 dt.duration_trunc(TimeDelta::minutes(5)).unwrap().to_string(),
628 "2012-12-12 18:20:00 UTC"
629 );
630 assert_eq!(
631 dt.duration_trunc(TimeDelta::minutes(10)).unwrap().to_string(),
632 "2012-12-12 18:20:00 UTC"
633 );
634 assert_eq!(
635 dt.duration_trunc(TimeDelta::minutes(30)).unwrap().to_string(),
636 "2012-12-12 18:00:00 UTC"
637 );
638 assert_eq!(
639 dt.duration_trunc(TimeDelta::hours(1)).unwrap().to_string(),
640 "2012-12-12 18:00:00 UTC"
641 );
642 assert_eq!(
643 dt.duration_trunc(TimeDelta::days(1)).unwrap().to_string(),
644 "2012-12-12 00:00:00 UTC"
645 );
646
647 let dt =
649 FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap();
650 assert_eq!(
651 dt.duration_trunc(TimeDelta::days(1)).unwrap().to_string(),
652 "2020-10-27 00:00:00 +01:00"
653 );
654 assert_eq!(
655 dt.duration_trunc(TimeDelta::weeks(1)).unwrap().to_string(),
656 "2020-10-22 00:00:00 +01:00"
657 );
658
659 let dt =
661 FixedOffset::west_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap();
662 assert_eq!(
663 dt.duration_trunc(TimeDelta::days(1)).unwrap().to_string(),
664 "2020-10-27 00:00:00 -01:00"
665 );
666 assert_eq!(
667 dt.duration_trunc(TimeDelta::weeks(1)).unwrap().to_string(),
668 "2020-10-22 00:00:00 -01:00"
669 );
670 }
671
672 #[test]
673 fn test_duration_trunc_naive() {
674 let dt = Utc
675 .from_local_datetime(
676 &NaiveDate::from_ymd_opt(2016, 12, 31)
677 .unwrap()
678 .and_hms_nano_opt(23, 59, 59, 175_500_000)
679 .unwrap(),
680 )
681 .unwrap()
682 .naive_utc();
683
684 assert_eq!(
685 dt.duration_trunc(TimeDelta::milliseconds(10)).unwrap().to_string(),
686 "2016-12-31 23:59:59.170"
687 );
688
689 let dt = Utc
691 .from_local_datetime(
692 &NaiveDate::from_ymd_opt(2012, 12, 12)
693 .unwrap()
694 .and_hms_milli_opt(18, 22, 30, 0)
695 .unwrap(),
696 )
697 .unwrap()
698 .naive_utc();
699 assert_eq!(
700 dt.duration_trunc(TimeDelta::minutes(5)).unwrap().to_string(),
701 "2012-12-12 18:20:00"
702 );
703 let dt = Utc
705 .from_local_datetime(
706 &NaiveDate::from_ymd_opt(2012, 12, 12)
707 .unwrap()
708 .and_hms_milli_opt(18, 22, 29, 999)
709 .unwrap(),
710 )
711 .unwrap()
712 .naive_utc();
713 assert_eq!(
714 dt.duration_trunc(TimeDelta::minutes(5)).unwrap().to_string(),
715 "2012-12-12 18:20:00"
716 );
717 assert_eq!(
718 dt.duration_trunc(TimeDelta::minutes(10)).unwrap().to_string(),
719 "2012-12-12 18:20:00"
720 );
721 assert_eq!(
722 dt.duration_trunc(TimeDelta::minutes(30)).unwrap().to_string(),
723 "2012-12-12 18:00:00"
724 );
725 assert_eq!(
726 dt.duration_trunc(TimeDelta::hours(1)).unwrap().to_string(),
727 "2012-12-12 18:00:00"
728 );
729 assert_eq!(
730 dt.duration_trunc(TimeDelta::days(1)).unwrap().to_string(),
731 "2012-12-12 00:00:00"
732 );
733 }
734
735 #[test]
736 fn test_duration_trunc_pre_epoch() {
737 let dt = Utc.with_ymd_and_hms(1969, 12, 12, 12, 12, 12).unwrap();
738 assert_eq!(
739 dt.duration_trunc(TimeDelta::minutes(10)).unwrap().to_string(),
740 "1969-12-12 12:10:00 UTC"
741 );
742 }
743
744 #[test]
745 fn issue1010() {
746 let dt = NaiveDateTime::from_timestamp_opt(-4_227_854_320, 678_774_288).unwrap();
747 let span = TimeDelta::microseconds(-7_019_067_213_869_040);
748 assert_eq!(dt.duration_trunc(span), Err(RoundingError::DurationExceedsLimit));
749
750 let dt = NaiveDateTime::from_timestamp_opt(320_041_586, 920_103_021).unwrap();
751 let span = TimeDelta::nanoseconds(-8_923_838_508_697_114_584);
752 assert_eq!(dt.duration_round(span), Err(RoundingError::DurationExceedsLimit));
753
754 let dt = NaiveDateTime::from_timestamp_opt(-2_621_440, 0).unwrap();
755 let span = TimeDelta::nanoseconds(-9_223_372_036_854_771_421);
756 assert_eq!(dt.duration_round(span), Err(RoundingError::DurationExceedsLimit));
757 }
758
759 #[test]
760 fn test_duration_trunc_close_to_epoch() {
761 let span = TimeDelta::minutes(15);
762
763 let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_opt(0, 0, 15).unwrap();
764 assert_eq!(dt.duration_trunc(span).unwrap().to_string(), "1970-01-01 00:00:00");
765
766 let dt = NaiveDate::from_ymd_opt(1969, 12, 31).unwrap().and_hms_opt(23, 59, 45).unwrap();
767 assert_eq!(dt.duration_trunc(span).unwrap().to_string(), "1969-12-31 23:45:00");
768 }
769
770 #[test]
771 fn test_duration_round_close_to_epoch() {
772 let span = TimeDelta::minutes(15);
773
774 let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_opt(0, 0, 15).unwrap();
775 assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00");
776
777 let dt = NaiveDate::from_ymd_opt(1969, 12, 31).unwrap().and_hms_opt(23, 59, 45).unwrap();
778 assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00");
779 }
780
781 #[test]
782 fn test_duration_round_close_to_min_max() {
783 let span = TimeDelta::nanoseconds(i64::MAX);
784
785 let dt = NaiveDateTime::from_timestamp_nanos(i64::MIN / 2 - 1).unwrap();
786 assert_eq!(dt.duration_round(span).unwrap().to_string(), "1677-09-21 00:12:43.145224193");
787
788 let dt = NaiveDateTime::from_timestamp_nanos(i64::MIN / 2 + 1).unwrap();
789 assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00");
790
791 let dt = NaiveDateTime::from_timestamp_nanos(i64::MAX / 2 + 1).unwrap();
792 assert_eq!(dt.duration_round(span).unwrap().to_string(), "2262-04-11 23:47:16.854775807");
793
794 let dt = NaiveDateTime::from_timestamp_nanos(i64::MAX / 2 - 1).unwrap();
795 assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00");
796 }
797}