1use std::cmp::Ordering;
9use std::fmt::Display;
10use std::hash::{Hash, Hasher};
11use std::ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign};
12
13use TimeUnit::*;
14#[cfg(feature = "serde")]
15use serde::{Deserialize, Serialize};
16
17use crate::error::TryFromDurationError;
18
19pub const DEFAULT_ID_NANO_SECOND: &str = "ns";
21pub const DEFAULT_ID_MICRO_SECOND: &str = "Ms";
23pub const DEFAULT_ID_MILLI_SECOND: &str = "ms";
25pub const DEFAULT_ID_SECOND: &str = "s";
27pub const DEFAULT_ID_MINUTE: &str = "m";
29pub const DEFAULT_ID_HOUR: &str = "h";
31pub const DEFAULT_ID_DAY: &str = "d";
33pub const DEFAULT_ID_WEEK: &str = "w";
35pub const DEFAULT_ID_MONTH: &str = "M";
37pub const DEFAULT_ID_YEAR: &str = "y";
39
40pub(crate) const DEFAULT_TIME_UNIT: TimeUnit = Second;
41
42const SECS_PER_MINUTE: u64 = 60;
43const SECS_PER_HOUR: u64 = 3600;
44const SECS_PER_DAY: u64 = 86400;
45const SECS_PER_WEEK: u64 = 86400 * 7;
46
47const NANOS_PER_MILLI: i32 = 1_000_000;
48const NANOS_PER_MICRO: i32 = 1_000;
49
50const MILLIS_PER_SEC: i128 = 1_000;
51const MICROS_PER_SEC: i128 = 1_000_000;
52const NANOS_PER_SEC: i128 = 1_000_000_000;
53
54#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
80#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
81pub enum TimeUnit {
82 NanoSecond,
85 MicroSecond,
87 MilliSecond,
89 Second,
92 Minute,
94 Hour,
96 Day,
98 Week,
100 Month,
102 Year,
105}
106
107impl Default for TimeUnit {
108 fn default() -> Self {
109 DEFAULT_TIME_UNIT
110 }
111}
112
113impl TimeUnit {
114 pub const fn default_identifier(&self) -> &'static str {
116 match self {
117 NanoSecond => DEFAULT_ID_NANO_SECOND,
118 MicroSecond => DEFAULT_ID_MICRO_SECOND,
119 MilliSecond => DEFAULT_ID_MILLI_SECOND,
120 Second => DEFAULT_ID_SECOND,
121 Minute => DEFAULT_ID_MINUTE,
122 Hour => DEFAULT_ID_HOUR,
123 Day => DEFAULT_ID_DAY,
124 Week => DEFAULT_ID_WEEK,
125 Month => DEFAULT_ID_MONTH,
126 Year => DEFAULT_ID_YEAR,
127 }
128 }
129
130 pub const fn multiplier(&self) -> Multiplier {
140 const MULTIPLIERS: [Multiplier; 10] = [
141 Multiplier(1, -9),
142 Multiplier(1, -6),
143 Multiplier(1, -3),
144 Multiplier(1, 0),
145 Multiplier(60, 0),
146 Multiplier(3_600, 0),
147 Multiplier(86_400, 0),
148 Multiplier(604_800, 0),
149 Multiplier(2_629_800, 0), Multiplier(31_557_600, 0), ];
152 MULTIPLIERS[*self as usize]
153 }
154}
155
156pub trait TimeUnitsLike {
197 fn is_empty(&self) -> bool;
247
248 fn get(&self, identifier: &str) -> Option<(TimeUnit, Multiplier)>;
358}
359
360#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
375#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
376pub struct Multiplier(pub i64, pub i16);
377
378impl Default for Multiplier {
379 fn default() -> Self {
380 Self(1, 0)
381 }
382}
383
384impl Multiplier {
385 #[inline]
396 pub const fn coefficient(&self) -> i64 {
397 self.0
398 }
399
400 #[inline]
411 pub const fn exponent(&self) -> i16 {
412 self.1
413 }
414
415 #[inline]
426 pub const fn is_negative(&self) -> bool {
427 !self.is_positive()
428 }
429
430 #[inline]
441 pub const fn is_positive(&self) -> bool {
442 self.0 == 0 || self.0.is_positive()
443 }
444
445 #[inline]
468 pub const fn checked_mul(&self, rhs: Self) -> Option<Self> {
469 if let Some(coefficient) = self.0.checked_mul(rhs.0) {
470 if let Some(exponent) = self.1.checked_add(rhs.1) {
471 return Some(Self(coefficient, exponent));
472 }
473 }
474 None
475 }
476
477 #[inline]
493 pub const fn saturating_neg(&self) -> Self {
494 Self(self.0.saturating_neg(), self.1)
495 }
496}
497
498impl Mul for Multiplier {
499 type Output = Self;
500
501 fn mul(self, rhs: Self) -> Self::Output {
502 self.checked_mul(rhs)
503 .expect("Multiplier: Overflow when multiplying")
504 }
505}
506
507pub trait SaturatingInto<T>: Sized {
509 fn saturating_into(self) -> T;
511}
512
513#[derive(Debug, Eq, Clone, Copy, Default)]
548#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
549pub struct Duration {
550 is_negative: bool,
551 inner: std::time::Duration,
552}
553
554impl Duration {
555 pub const ZERO: Self = Self {
566 is_negative: false,
567 inner: std::time::Duration::ZERO,
568 };
569
570 pub const MIN: Self = Self {
581 is_negative: true,
582 inner: std::time::Duration::MAX,
583 };
584
585 pub const MAX: Self = Self {
596 is_negative: false,
597 inner: std::time::Duration::MAX,
598 };
599
600 pub const fn from_std(is_negative: bool, inner: std::time::Duration) -> Self {
614 Self { is_negative, inner }
615 }
616
617 pub const fn positive(secs: u64, nanos: u32) -> Self {
636 Self {
637 is_negative: false,
638 inner: std::time::Duration::new(secs, nanos),
639 }
640 }
641
642 pub const fn negative(secs: u64, nanos: u32) -> Self {
658 Self {
659 is_negative: true,
660 inner: std::time::Duration::new(secs, nanos),
661 }
662 }
663
664 const fn as_whole(&self, factor: u64) -> i64 {
666 debug_assert!(factor > 1);
667
668 #[allow(clippy::cast_possible_wrap)]
669 let result = (self.inner.as_secs() / factor) as i64;
670 if self.is_negative { -result } else { result }
671 }
672
673 #[inline]
686 pub const fn as_weeks(&self) -> i64 {
687 self.as_whole(SECS_PER_WEEK)
688 }
689
690 #[inline]
703 pub const fn as_days(&self) -> i64 {
704 self.as_whole(SECS_PER_DAY)
705 }
706
707 #[inline]
720 pub const fn as_hours(&self) -> i64 {
721 self.as_whole(SECS_PER_HOUR)
722 }
723
724 #[inline]
737 pub const fn as_minutes(&self) -> i64 {
738 self.as_whole(SECS_PER_MINUTE)
739 }
740
741 #[inline]
754 pub const fn as_seconds(&self) -> i128 {
755 let seconds = self.inner.as_secs() as i128;
756 if self.is_negative { -seconds } else { seconds }
757 }
758
759 #[inline]
772 pub const fn as_millis(&self) -> i128 {
773 self.as_seconds() * MILLIS_PER_SEC + self.subsec_millis() as i128
774 }
775
776 #[inline]
789 pub const fn as_micros(&self) -> i128 {
790 self.as_seconds() * MICROS_PER_SEC + self.subsec_micros() as i128
791 }
792
793 #[inline]
806 pub const fn as_nanos(&self) -> i128 {
807 self.as_seconds() * NANOS_PER_SEC + self.subsec_nanos() as i128
808 }
809
810 #[inline]
823 pub const fn subsec_millis(&self) -> i32 {
824 self.subsec_nanos() / NANOS_PER_MILLI
825 }
826
827 #[inline]
839 pub const fn subsec_micros(&self) -> i32 {
840 self.subsec_nanos() / NANOS_PER_MICRO
841 }
842
843 #[inline]
855 pub const fn subsec_nanos(&self) -> i32 {
856 #[allow(clippy::cast_possible_wrap)]
857 let nanos = self.inner.subsec_nanos() as i32;
858 if self.is_negative { -nanos } else { nanos }
859 }
860
861 fn extract_i64(&mut self, factor: u64) -> i64 {
862 debug_assert!(factor > 1);
863
864 let secs = self.inner.as_secs();
865 let extracted = i64::try_from(secs / factor).unwrap();
866 if extracted > 0 {
867 self.inner = std::time::Duration::new(secs % factor, self.inner.subsec_nanos());
868 if self.is_negative {
869 extracted.neg()
870 } else {
871 extracted
872 }
873 } else {
874 0
875 }
876 }
877
878 #[inline]
894 pub fn extract_weeks(&mut self) -> i64 {
895 self.extract_i64(SECS_PER_WEEK)
896 }
897
898 #[inline]
914 pub fn extract_days(&mut self) -> i64 {
915 self.extract_i64(SECS_PER_DAY)
916 }
917
918 #[inline]
934 pub fn extract_hours(&mut self) -> i64 {
935 self.extract_i64(SECS_PER_HOUR)
936 }
937
938 #[inline]
954 pub fn extract_minutes(&mut self) -> i64 {
955 self.extract_i64(SECS_PER_MINUTE)
956 }
957
958 #[inline]
974 pub fn extract_seconds(&mut self) -> i128 {
975 let extracted = self.as_seconds();
976 if extracted == 0 {
977 0
978 } else {
979 self.inner = std::time::Duration::new(0, self.inner.subsec_nanos());
980 extracted
981 }
982 }
983
984 #[inline]
998 pub const fn is_negative(&self) -> bool {
999 self.is_negative
1000 }
1001
1002 #[inline]
1016 pub const fn is_positive(&self) -> bool {
1017 !self.is_negative
1018 }
1019
1020 #[inline]
1037 pub const fn is_zero(&self) -> bool {
1038 self.inner.is_zero()
1039 }
1040
1041 #[inline]
1060 pub const fn abs(&self) -> Self {
1061 Self::from_std(false, self.inner)
1062 }
1063
1064 pub fn checked_add(&self, other: Self) -> Option<Self> {
1085 match (
1086 self.is_negative,
1087 other.is_negative,
1088 self.inner.cmp(&other.inner),
1089 ) {
1090 (true, true, _) => self
1091 .inner
1092 .checked_add(other.inner)
1093 .map(|d| Self::from_std(true, d)),
1094 (true, false, Ordering::Equal) | (false, true, Ordering::Equal) => Some(Self::ZERO),
1095 (true, false, Ordering::Greater) => {
1096 Some(Self::from_std(true, self.inner.checked_sub(other.inner)?))
1097 }
1098 (true, false, Ordering::Less) => {
1099 Some(Self::from_std(false, other.inner.checked_sub(self.inner)?))
1100 }
1101 (false, true, Ordering::Greater) => {
1102 Some(Self::from_std(false, self.inner.checked_sub(other.inner)?))
1103 }
1104 (false, true, Ordering::Less) => {
1105 Some(Self::from_std(true, other.inner.checked_sub(self.inner)?))
1106 }
1107 (false, false, _) => self
1108 .inner
1109 .checked_add(other.inner)
1110 .map(|d| Self::from_std(false, d)),
1111 }
1112 }
1113
1114 #[inline]
1131 pub fn checked_sub(&self, other: Self) -> Option<Self> {
1132 self.checked_add(other.neg())
1133 }
1134
1135 pub fn saturating_add(&self, other: Self) -> Self {
1153 match self.checked_add(other) {
1154 Some(d) => d,
1155 None if self.is_negative => Self::MIN,
1158 None => Self::MAX,
1159 }
1160 }
1161
1162 #[inline]
1180 pub fn saturating_sub(&self, other: Self) -> Self {
1181 self.saturating_add(other.neg())
1182 }
1183}
1184
1185impl Display for Duration {
1186 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1187 const YEAR: u64 = Year.multiplier().0.unsigned_abs();
1188 const MONTH: u64 = Month.multiplier().0.unsigned_abs();
1189 const WEEK: u64 = Week.multiplier().0.unsigned_abs();
1190 const DAY: u64 = Day.multiplier().0.unsigned_abs();
1191 const HOUR: u64 = Hour.multiplier().0.unsigned_abs();
1192 const MINUTE: u64 = Minute.multiplier().0.unsigned_abs();
1193 const MILLIS_PER_NANO: u32 = 1_000_000;
1194 const MICROS_PER_NANO: u32 = 1_000;
1195
1196 if self.is_zero() {
1197 return f.write_str("0ns");
1198 }
1199
1200 let mut result = Vec::with_capacity(10);
1201 let mut secs = self.inner.as_secs();
1202 if secs > 0 {
1203 if secs >= YEAR {
1204 result.push(format!("{}y", secs / YEAR));
1205 secs %= YEAR;
1206 }
1207 if secs >= MONTH {
1208 result.push(format!("{}M", secs / MONTH));
1209 secs %= MONTH;
1210 }
1211 if secs >= WEEK {
1212 result.push(format!("{}w", secs / WEEK));
1213 secs %= WEEK;
1214 }
1215 if secs >= DAY {
1216 result.push(format!("{}d", secs / DAY));
1217 secs %= DAY;
1218 }
1219 if secs >= HOUR {
1220 result.push(format!("{}h", secs / HOUR));
1221 secs %= HOUR;
1222 }
1223 if secs >= MINUTE {
1224 result.push(format!("{}m", secs / MINUTE));
1225 secs %= MINUTE;
1226 }
1227 if secs >= 1 {
1228 result.push(format!("{secs}s"));
1229 }
1230 }
1231
1232 let mut nanos = self.inner.subsec_nanos();
1233 if nanos > 0 {
1234 if nanos >= MILLIS_PER_NANO {
1235 result.push(format!("{}ms", nanos / MILLIS_PER_NANO));
1236 nanos %= MILLIS_PER_NANO;
1237 }
1238 if nanos >= MICROS_PER_NANO {
1239 result.push(format!("{}Ms", nanos / MICROS_PER_NANO));
1240 nanos %= MICROS_PER_NANO;
1241 }
1242 if nanos >= 1 {
1243 result.push(format!("{nanos}ns"));
1244 }
1245 }
1246
1247 if self.is_negative() {
1248 f.write_str(&format!("-{}", result.join(" -")))
1249 } else {
1250 f.write_str(&result.join(" "))
1251 }
1252 }
1253}
1254
1255impl Add for Duration {
1256 type Output = Self;
1257
1258 fn add(self, rhs: Self) -> Self::Output {
1259 self.checked_add(rhs)
1260 .expect("Overflow when adding duration")
1261 }
1262}
1263
1264impl AddAssign for Duration {
1265 fn add_assign(&mut self, rhs: Self) {
1266 *self = *self + rhs;
1267 }
1268}
1269
1270impl Sub for Duration {
1271 type Output = Self;
1272
1273 fn sub(self, rhs: Self) -> Self::Output {
1274 self.checked_sub(rhs)
1275 .expect("Overflow when subtracting duration")
1276 }
1277}
1278
1279impl SubAssign for Duration {
1280 fn sub_assign(&mut self, rhs: Self) {
1281 *self = *self - rhs;
1282 }
1283}
1284
1285impl Neg for Duration {
1286 type Output = Self;
1287
1288 fn neg(self) -> Self::Output {
1289 Self {
1290 is_negative: self.is_negative ^ true,
1291 inner: self.inner,
1292 }
1293 }
1294}
1295
1296impl PartialEq for Duration {
1297 fn eq(&self, other: &Self) -> bool {
1298 (self.is_zero() && other.is_zero())
1299 || (self.is_negative == other.is_negative && self.inner == other.inner)
1300 }
1301}
1302
1303impl Hash for Duration {
1304 fn hash<H: Hasher>(&self, state: &mut H) {
1305 if self.is_zero() {
1306 false.hash(state);
1307 } else {
1308 self.is_negative.hash(state);
1309 }
1310 self.inner.hash(state);
1311 }
1312}
1313
1314impl PartialOrd for Duration {
1315 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1316 Some(self.cmp(other))
1317 }
1318}
1319
1320impl Ord for Duration {
1321 fn cmp(&self, other: &Self) -> Ordering {
1322 match (self.is_negative, other.is_negative) {
1323 (true, true) => other.inner.cmp(&self.inner),
1324 (true, false) | (false, true) if self.is_zero() && other.is_zero() => Ordering::Equal,
1325 (true, false) => Ordering::Less,
1326 (false, true) => Ordering::Greater,
1327 (false, false) => self.inner.cmp(&other.inner),
1328 }
1329 }
1330}
1331
1332impl From<std::time::Duration> for Duration {
1334 fn from(duration: std::time::Duration) -> Self {
1335 Self {
1336 is_negative: false,
1337 inner: duration,
1338 }
1339 }
1340}
1341
1342impl SaturatingInto<std::time::Duration> for Duration {
1343 fn saturating_into(self) -> std::time::Duration {
1344 self.try_into().unwrap_or_else(|error| match error {
1345 TryFromDurationError::NegativeDuration => std::time::Duration::ZERO,
1346 _ => unreachable!(), })
1348 }
1349}
1350
1351impl TryFrom<Duration> for std::time::Duration {
1353 type Error = TryFromDurationError;
1354
1355 fn try_from(duration: Duration) -> Result<Self, Self::Error> {
1356 if !duration.is_zero() && duration.is_negative {
1357 Err(TryFromDurationError::NegativeDuration)
1358 } else {
1359 Ok(duration.inner)
1360 }
1361 }
1362}
1363
1364#[cfg(feature = "time")]
1365impl From<time::Duration> for Duration {
1369 fn from(duration: time::Duration) -> Self {
1370 Self {
1371 is_negative: duration.is_negative(),
1372 inner: std::time::Duration::new(
1373 duration.whole_seconds().unsigned_abs(),
1374 duration.subsec_nanoseconds().unsigned_abs(),
1375 ),
1376 }
1377 }
1378}
1379
1380#[cfg(feature = "time")]
1381impl TryFrom<Duration> for time::Duration {
1385 type Error = TryFromDurationError;
1386
1387 fn try_from(duration: Duration) -> Result<Self, Self::Error> {
1388 (&duration).try_into()
1389 }
1390}
1391
1392#[cfg(feature = "time")]
1393impl TryFrom<&Duration> for time::Duration {
1397 type Error = TryFromDurationError;
1398
1399 fn try_from(duration: &Duration) -> Result<Self, Self::Error> {
1400 #[allow(clippy::cast_possible_wrap)]
1401 match (duration.is_negative, duration.inner.as_secs()) {
1402 (true, secs) if secs > i64::MIN.unsigned_abs() => {
1403 Err(TryFromDurationError::NegativeOverflow)
1404 }
1405 (true, secs) => Ok(Self::new(
1406 secs.wrapping_neg() as i64,
1407 -(duration.inner.subsec_nanos() as i32),
1408 )),
1409 (false, secs) if secs > i64::MAX as u64 => Err(TryFromDurationError::PositiveOverflow),
1410 (false, secs) => Ok(Self::new(secs as i64, duration.inner.subsec_nanos() as i32)),
1411 }
1412 }
1413}
1414
1415#[cfg(feature = "time")]
1416impl SaturatingInto<time::Duration> for Duration {
1417 fn saturating_into(self) -> time::Duration {
1418 self.try_into().unwrap_or_else(|error| match error {
1419 TryFromDurationError::PositiveOverflow => time::Duration::MAX,
1420 TryFromDurationError::NegativeOverflow => time::Duration::MIN,
1421 TryFromDurationError::NegativeDuration => unreachable!(), })
1423 }
1424}
1425
1426#[cfg(feature = "chrono")]
1427impl TryFrom<Duration> for chrono::Duration {
1429 type Error = TryFromDurationError;
1430
1431 fn try_from(duration: Duration) -> Result<Self, Self::Error> {
1432 (&duration).try_into()
1433 }
1434}
1435
1436#[cfg(feature = "chrono")]
1437impl TryFrom<&Duration> for chrono::Duration {
1439 type Error = TryFromDurationError;
1440
1441 fn try_from(duration: &Duration) -> Result<Self, Self::Error> {
1442 const MAX: chrono::Duration = chrono::TimeDelta::MAX;
1443 const MIN: chrono::Duration = chrono::TimeDelta::MIN;
1444
1445 #[allow(clippy::cast_possible_wrap)]
1446 match (duration.is_negative, duration.inner.as_secs()) {
1447 (true, secs) if secs > MIN.num_seconds().unsigned_abs() => {
1448 Err(TryFromDurationError::NegativeOverflow)
1449 }
1450 (true, secs) => {
1451 let nanos = Self::nanoseconds((i64::from(duration.inner.subsec_nanos())).neg());
1452 Self::seconds((secs as i64).neg())
1453 .checked_add(&nanos)
1454 .ok_or(TryFromDurationError::NegativeOverflow)
1455 }
1456 (false, secs) if secs > MAX.num_seconds().unsigned_abs() => {
1457 Err(TryFromDurationError::PositiveOverflow)
1458 }
1459 (false, secs) => {
1460 let nanos = Self::nanoseconds(i64::from(duration.inner.subsec_nanos()));
1461 Self::seconds(secs as i64)
1462 .checked_add(&nanos)
1463 .ok_or(TryFromDurationError::PositiveOverflow)
1464 }
1465 }
1466 }
1467}
1468
1469#[cfg(feature = "chrono")]
1470impl SaturatingInto<chrono::Duration> for Duration {
1471 fn saturating_into(self) -> chrono::Duration {
1472 self.try_into().unwrap_or_else(|error| match error {
1473 TryFromDurationError::PositiveOverflow => chrono::TimeDelta::MAX,
1474 TryFromDurationError::NegativeOverflow => chrono::TimeDelta::MIN,
1475 TryFromDurationError::NegativeDuration => unreachable!(), })
1477 }
1478}
1479
1480#[cfg(feature = "chrono")]
1481impl From<chrono::Duration> for Duration {
1483 fn from(duration: chrono::Duration) -> Self {
1484 duration.to_std().map_or_else(
1485 |_| Self {
1486 is_negative: true,
1487 inner: duration.abs().to_std().unwrap(),
1488 },
1489 |inner| Self {
1490 is_negative: false,
1491 inner,
1492 },
1493 )
1494 }
1495}
1496
1497#[cfg(test)]
1498mod tests {
1499 use std::collections::hash_map::DefaultHasher;
1500 use std::time::Duration as StdDuration;
1501
1502 #[cfg(feature = "chrono")]
1503 use chrono::Duration as ChronoDuration;
1504 use rstest::rstest;
1505 use rstest_reuse::{apply, template};
1506 #[cfg(feature = "serde")]
1507 use serde_test::{Token, assert_tokens};
1508
1509 use super::*;
1510
1511 #[cfg(feature = "chrono")]
1512 const CHRONO_MIN_DURATION: Duration =
1513 Duration::negative(i64::MIN.unsigned_abs() / 1000, 807_000_000);
1514 #[cfg(feature = "chrono")]
1515 const CHRONO_MAX_DURATION: Duration = Duration::positive(i64::MAX as u64 / 1000, 807_000_000);
1516
1517 const YEAR_AS_SECS: u64 = 60 * 60 * 24 * 365 + 60 * 60 * 24 / 4; const MONTH_AS_SECS: u64 = YEAR_AS_SECS / 12;
1519
1520 #[cfg(feature = "serde")]
1521 #[test]
1522 fn test_time_unit_serde() {
1523 let time_unit = TimeUnit::Day;
1524
1525 assert_tokens(
1526 &time_unit,
1527 &[
1528 Token::Enum { name: "TimeUnit" },
1529 Token::Str("Day"),
1530 Token::Unit,
1531 ],
1532 );
1533 }
1534
1535 #[cfg(feature = "serde")]
1536 #[test]
1537 fn test_serde_multiplier() {
1538 let multiplier = Multiplier(1, 2);
1539
1540 assert_tokens(
1541 &multiplier,
1542 &[
1543 Token::TupleStruct {
1544 name: "Multiplier",
1545 len: 2,
1546 },
1547 Token::I64(1),
1548 Token::I16(2),
1549 Token::TupleStructEnd,
1550 ],
1551 );
1552 }
1553
1554 #[cfg(feature = "serde")]
1555 #[test]
1556 fn test_serde_duration() {
1557 let duration = Duration::positive(1, 2);
1558
1559 assert_tokens(
1560 &duration,
1561 &[
1562 Token::Struct {
1563 name: "Duration",
1564 len: 2,
1565 },
1566 Token::Str("is_negative"),
1567 Token::Bool(false),
1568 Token::Str("inner"),
1569 Token::Struct {
1570 name: "Duration",
1571 len: 2,
1572 },
1573 Token::Str("secs"),
1574 Token::U64(1),
1575 Token::Str("nanos"),
1576 Token::U32(2),
1577 Token::StructEnd,
1578 Token::StructEnd,
1579 ],
1580 );
1581 }
1582
1583 #[rstest]
1584 #[case::nano_second(NanoSecond, "ns")]
1585 #[case::micro_second(MicroSecond, "Ms")]
1586 #[case::milli_second(MilliSecond, "ms")]
1587 #[case::second(Second, "s")]
1588 #[case::minute(Minute, "m")]
1589 #[case::hour(Hour, "h")]
1590 #[case::day(Day, "d")]
1591 #[case::week(Week, "w")]
1592 #[case::month(Month, "M")]
1593 #[case::year(Year, "y")]
1594 fn test_time_unit_default_identifier(#[case] time_unit: TimeUnit, #[case] expected: &str) {
1595 assert_eq!(time_unit.default_identifier(), expected);
1596 }
1597
1598 #[rstest]
1599 #[case::nano_second(NanoSecond, Multiplier(1, -9))]
1600 #[case::micro_second(MicroSecond, Multiplier(1, -6))]
1601 #[case::milli_second(MilliSecond, Multiplier(1, -3))]
1602 #[case::second(Second, Multiplier(1, 0))]
1603 #[case::minute(Minute, Multiplier(60, 0))]
1604 #[case::hour(Hour, Multiplier(60 * 60, 0))]
1605 #[case::day(Day, Multiplier(60 * 60 * 24, 0))]
1606 #[case::week(Week, Multiplier(60 * 60 * 24 * 7, 0))]
1607 #[case::month(Month, Multiplier((60 * 60 * 24 * 365 + 60 * 60 * 24 / 4) / 12, 0))] #[case::year(Year, Multiplier(60 * 60 * 24 * 365 + 60 * 60 * 24 / 4, 0))] fn test_time_unit_multiplier(#[case] time_unit: TimeUnit, #[case] expected: Multiplier) {
1610 assert_eq!(time_unit.multiplier(), expected);
1611 }
1612
1613 #[test]
1614 fn test_multiplier_get_coefficient() {
1615 let multi = Multiplier(1234, 0);
1616 assert_eq!(multi.coefficient(), 1234);
1617 }
1618
1619 #[test]
1620 fn test_multiplier_get_exponent() {
1621 let multi = Multiplier(0, 1234);
1622 assert_eq!(multi.exponent(), 1234);
1623 }
1624
1625 #[rstest]
1626 #[case::zero(Multiplier(0, 0), false)]
1627 #[case::negative(Multiplier(-1, 0), true)]
1628 #[case::positive(Multiplier(1, 0), false)]
1629 #[case::negative_exponent(Multiplier(1, -1), false)]
1630 #[case::positive_exponent(Multiplier(-1, 1), true)]
1631 fn test_multiplier_is_negative_and_is_positive(
1632 #[case] multi: Multiplier,
1633 #[case] expected: bool,
1634 ) {
1635 assert_eq!(multi.is_negative(), expected);
1636 assert_eq!(multi.is_positive(), !expected);
1637 }
1638
1639 #[rstest]
1640 #[case::nano_second(NanoSecond, Multiplier(i64::MAX, i16::MIN + 9))]
1641 #[case::micro_second(MicroSecond, Multiplier(i64::MAX, i16::MIN + 6))]
1642 #[case::milli_second(MilliSecond, Multiplier(i64::MAX, i16::MIN + 3))]
1643 #[case::second(Second, Multiplier(i64::MAX, i16::MIN))]
1644 #[case::minute(Minute, Multiplier(i64::MAX / 60, i16::MIN))]
1645 #[case::hour(Hour, Multiplier(i64::MAX / (60 * 60), i16::MIN))]
1646 #[case::day(Day, Multiplier(i64::MAX / (60 * 60 * 24), i16::MIN))]
1647 #[case::week(Week, Multiplier(i64::MAX / (60 * 60 * 24 * 7), i16::MIN))]
1648 #[case::month(Month, Multiplier(3_507_252_276_543, i16::MIN))]
1649 #[case::year(Year, Multiplier(292_271_023_045, i16::MIN))]
1650 fn test_multiplier_multiplication_barely_no_panic(
1651 #[case] time_unit: TimeUnit,
1652 #[case] multiplier: Multiplier,
1653 ) {
1654 _ = time_unit.multiplier() * multiplier;
1655 }
1656
1657 #[rstest]
1658 #[case::nano_second(NanoSecond, Multiplier(i64::MAX, i16::MIN + 8))]
1659 #[case::micro_second(MicroSecond, Multiplier(i64::MAX, i16::MIN + 4))]
1660 #[case::milli_second(MilliSecond, Multiplier(i64::MAX, i16::MIN + 2))]
1661 #[case::minute(Minute, Multiplier(i64::MAX / 60 + 1, i16::MIN))]
1662 #[case::hour(Hour, Multiplier(i64::MAX / (60 * 60) + 1, i16::MIN))]
1663 #[case::day(Day, Multiplier(i64::MAX / (60 * 60 * 24) + 1, i16::MIN))]
1664 #[case::week(Week, Multiplier(i64::MAX / (60 * 60 * 24 * 7) + 1, i16::MIN))]
1665 #[case::month(Month, Multiplier(3_507_252_276_543 + 1, i16::MIN))]
1666 #[case::year(Year, Multiplier(292_271_023_045 + 1, i16::MIN))]
1667 #[should_panic(expected = "Overflow")]
1668 fn test_multiplier_multiplication_then_panic(
1669 #[case] time_unit: TimeUnit,
1670 #[case] multiplier: Multiplier,
1671 ) {
1672 _ = time_unit.multiplier() * multiplier;
1673 }
1674
1675 #[rstest]
1676 #[case::positive_zero(Duration::positive(0, 0), true)]
1677 #[case::negative_zero(Duration::negative(0, 0), false)] #[case::positive_one(Duration::positive(1, 0), true)]
1679 #[case::negative_one(Duration::negative(1, 0), false)]
1680 fn test_fundu_duration_is_positive_and_is_negative(
1681 #[case] duration: Duration,
1682 #[case] expected: bool,
1683 ) {
1684 assert_eq!(duration.is_positive(), expected);
1685 assert_eq!(duration.is_negative(), !expected);
1686 }
1687
1688 #[rstest]
1689 #[case::positive_zero(Duration::positive(0, 0), Duration::positive(0, 0))]
1690 #[case::negative_zero(Duration::negative(0, 0), Duration::positive(0, 0))]
1691 #[case::positive_one(Duration::positive(1, 0), Duration::positive(1, 0))]
1692 #[case::positive_one_nano(Duration::positive(0, 1), Duration::positive(0, 1))]
1693 #[case::negative_one(Duration::negative(1, 0), Duration::positive(1, 0))]
1694 #[case::negative_one_nano(Duration::negative(0, 1), Duration::positive(0, 1))]
1695 #[case::negative_one_one(Duration::negative(1, 1), Duration::positive(1, 1))]
1696 fn test_fundu_duration_abs(#[case] duration: Duration, #[case] expected: Duration) {
1697 assert_eq!(duration.abs(), expected);
1698 }
1699
1700 #[rstest]
1701 #[case::zero(Duration::ZERO, "0ns")]
1702 #[case::nano_second(Duration::positive(0, 1), "1ns")]
1703 #[case::micro_second(Duration::positive(0, 1_000), "1Ms")]
1704 #[case::milli_second(Duration::positive(0, 1_000_000), "1ms")]
1705 #[case::second(Duration::positive(1, 0), "1s")]
1706 #[case::minute(Duration::positive(60, 0), "1m")]
1707 #[case::hour(Duration::positive(60 * 60, 0), "1h")]
1708 #[case::day(Duration::positive(60 * 60 * 24, 0), "1d")]
1709 #[case::week(Duration::positive(60 * 60 * 24 * 7, 0), "1w")]
1710 #[case::month(Duration::positive(MONTH_AS_SECS, 0), "1M")]
1711 #[case::year(Duration::positive(YEAR_AS_SECS, 0), "1y")]
1712 #[case::all_one(
1713 Duration::positive(
1714 YEAR_AS_SECS + MONTH_AS_SECS + 60 * 60 * 24 * 8 + 60 * 60 + 60 + 1,
1715 1_001_001
1716 ),
1717 "1y 1M 1w 1d 1h 1m 1s 1ms 1Ms 1ns"
1718 )]
1719 #[case::max(Duration::MAX, "584542046090y 7M 2w 1d 17h 30m 15s 999ms 999Ms 999ns")]
1720 fn test_fundu_display(#[case] duration: Duration, #[case] expected: &str) {
1721 assert_eq!(duration.to_string(), expected.to_owned());
1722 if duration.is_zero() {
1723 assert_eq!(duration.neg().to_string(), expected.to_owned());
1724 } else {
1725 assert_eq!(
1726 duration.neg().to_string(),
1727 format!("-{}", expected.replace(' ', " -"))
1728 );
1729 }
1730 }
1731
1732 #[template]
1733 #[rstest]
1734 #[case::both_standard_zero(Duration::ZERO, Duration::ZERO, Duration::ZERO)]
1735 #[case::both_positive_zero(
1736 Duration::positive(0, 0),
1737 Duration::positive(0, 0),
1738 Duration::positive(0, 0)
1739 )]
1740 #[case::both_negative_zero(Duration::negative(0, 0), Duration::negative(0, 0), Duration::ZERO)]
1741 #[case::one_add_zero(Duration::positive(1, 0), Duration::ZERO, Duration::positive(1, 0))]
1742 #[case::minus_one_add_zero(Duration::negative(1, 0), Duration::ZERO, Duration::negative(1, 0))]
1743 #[case::minus_one_add_plus_one(
1744 Duration::negative(1, 0),
1745 Duration::positive(1, 0),
1746 Duration::ZERO
1747 )]
1748 #[case::minus_one_add_plus_two_then_carry(
1749 Duration::negative(1, 0),
1750 Duration::positive(2, 0),
1751 Duration::positive(1, 0)
1752 )]
1753 #[case::minus_one_nano_add_one_then_carry(
1754 Duration::negative(0, 1),
1755 Duration::positive(1, 0),
1756 Duration::positive(0, 999_999_999)
1757 )]
1758 #[case::plus_one_nano_add_minus_one_then_carry(
1759 Duration::positive(0, 1),
1760 Duration::negative(1, 0),
1761 Duration::negative(0, 999_999_999)
1762 )]
1763 #[case::plus_one_add_minus_two_then_carry(
1764 Duration::positive(1, 0),
1765 Duration::negative(2, 0),
1766 Duration::negative(1, 0)
1767 )]
1768 #[case::one_sec_below_min_add_max(
1769 Duration::negative(u64::MAX - 1, 999_999_999),
1770 Duration::MAX,
1771 Duration::positive(1, 0),
1772 )]
1773 #[case::one_nano_below_min_add_max(
1774 Duration::negative(u64::MAX, 999_999_998),
1775 Duration::MAX,
1776 Duration::positive(0, 1)
1777 )]
1778 #[case::one_sec_below_max_add_min(
1779 Duration::positive(u64::MAX - 1, 999_999_999),
1780 Duration::MIN,
1781 Duration::negative(1, 0)
1782 )]
1783 #[case::one_nano_below_max_add_min(
1784 Duration::positive(u64::MAX, 999_999_998),
1785 Duration::MIN,
1786 Duration::negative(0, 1)
1787 )]
1788 #[case::min_and_max(Duration::MIN, Duration::MAX, Duration::ZERO)]
1789 fn test_fundu_duration_add_no_overflow_template(
1790 #[case] lhs: Duration,
1791 #[case] rhs: Duration,
1792 #[case] expected: Duration,
1793 ) {
1794 }
1795
1796 #[apply(test_fundu_duration_add_no_overflow_template)]
1797 fn test_fundu_duration_add(lhs: Duration, rhs: Duration, expected: Duration) {
1798 assert_eq!(lhs + rhs, expected);
1799 assert_eq!(rhs + lhs, expected);
1800 }
1801
1802 #[apply(test_fundu_duration_add_no_overflow_template)]
1803 fn test_fundu_duration_add_assign(lhs: Duration, rhs: Duration, expected: Duration) {
1804 let mut res = lhs;
1805 res += rhs;
1806 assert_eq!(res, expected);
1807 let mut res = rhs;
1808 res += lhs;
1809 assert_eq!(res, expected);
1810 }
1811
1812 #[apply(test_fundu_duration_add_no_overflow_template)]
1813 fn test_fundu_duration_checked_add(lhs: Duration, rhs: Duration, expected: Duration) {
1814 assert_eq!(lhs.checked_add(rhs), Some(expected));
1815 assert_eq!(rhs.checked_add(lhs), Some(expected));
1816 }
1817
1818 #[apply(test_fundu_duration_add_no_overflow_template)]
1819 fn test_fundu_duration_sub(lhs: Duration, rhs: Duration, expected: Duration) {
1820 assert_eq!(lhs - rhs.neg(), expected);
1821 assert_eq!(rhs - lhs.neg(), expected);
1822 }
1823
1824 #[apply(test_fundu_duration_add_no_overflow_template)]
1825 fn test_fundu_duration_sub_assign(lhs: Duration, rhs: Duration, expected: Duration) {
1826 let mut res = lhs;
1827 res -= rhs.neg();
1828 assert_eq!(res, expected);
1829 let mut res = rhs;
1830 res -= lhs.neg();
1831 assert_eq!(res, expected);
1832 }
1833
1834 #[apply(test_fundu_duration_add_no_overflow_template)]
1835 fn test_fundu_duration_checked_sub(lhs: Duration, rhs: Duration, expected: Duration) {
1836 assert_eq!(lhs.checked_sub(rhs.neg()), Some(expected));
1837 assert_eq!(rhs.checked_sub(lhs.neg()), Some(expected));
1838 }
1839
1840 #[template]
1841 #[rstest]
1842 #[case::min(Duration::MIN, Duration::MIN)]
1843 #[case::min_minus_one(Duration::MIN, Duration::negative(1, 0))]
1844 #[case::min_minus_one_nano(Duration::MIN, Duration::negative(0, 1))]
1845 #[case::max(Duration::MAX, Duration::MAX)]
1846 #[case::max_plus_one(Duration::MAX, Duration::positive(1, 0))]
1847 #[case::max_plus_one_nano(Duration::MAX, Duration::positive(0, 1))]
1848 #[case::positive_middle_nano_overflow(
1849 Duration::positive(u64::MAX / 2 + 1, 999_999_999 / 2 + 1),
1850 Duration::positive(u64::MAX / 2, 999_999_999 / 2 + 1)
1851 )]
1852 #[case::positive_middle_secs_overflow(
1853 Duration::positive(u64::MAX / 2 + 1, 999_999_999 / 2 + 1),
1854 Duration::positive(u64::MAX / 2 + 1, 999_999_999 / 2)
1855 )]
1856 #[case::negative_middle_nano_overflow(
1857 Duration::negative(u64::MAX / 2 + 1, 999_999_999 / 2 + 1),
1858 Duration::negative(u64::MAX / 2, 999_999_999 / 2 + 1)
1859 )]
1860 #[case::negative_middle_secs_overflow(
1861 Duration::negative(u64::MAX / 2 + 1, 999_999_999 / 2 + 1),
1862 Duration::negative(u64::MAX / 2 + 1, 999_999_999 / 2)
1863 )]
1864 fn test_fundu_duration_add_overflow_template(#[case] lhs: Duration, #[case] rhs: Duration) {}
1865
1866 #[apply(test_fundu_duration_add_overflow_template)]
1867 #[should_panic = "Overflow when adding duration"]
1868 fn test_fundu_duration_add_and_add_assign_then_overflow(mut lhs: Duration, rhs: Duration) {
1869 lhs += rhs;
1870 }
1871
1872 #[apply(test_fundu_duration_add_overflow_template)]
1873 #[should_panic = "Overflow when subtracting duration"]
1874 #[allow(clippy::no_effect)]
1875 fn test_fundu_duration_sub_and_sub_assign_then_overflow(mut lhs: Duration, rhs: Duration) {
1876 lhs -= rhs.neg();
1877 }
1878
1879 #[apply(test_fundu_duration_add_overflow_template)]
1880 fn test_fundu_duration_checked_add_then_overflow(lhs: Duration, rhs: Duration) {
1881 assert_eq!(lhs.checked_add(rhs), None);
1882 assert_eq!(rhs.checked_add(lhs), None);
1883 }
1884
1885 #[apply(test_fundu_duration_add_overflow_template)]
1886 fn test_fundu_duration_checked_sub_then_overflow(lhs: Duration, rhs: Duration) {
1887 assert_eq!(lhs.checked_sub(rhs.neg()), None);
1888 }
1889
1890 #[apply(test_fundu_duration_add_overflow_template)]
1891 fn test_fundu_duration_saturating_add_then_saturate(lhs: Duration, rhs: Duration) {
1892 let expected = if lhs.checked_add(rhs).is_none() && lhs.is_positive() && rhs.is_positive() {
1893 Duration::MAX
1894 } else {
1895 Duration::MIN
1896 };
1897 assert_eq!(lhs.saturating_add(rhs), expected);
1898 assert_eq!(rhs.saturating_add(lhs), expected);
1899 }
1900
1901 #[apply(test_fundu_duration_add_overflow_template)]
1902 fn test_fundu_duration_saturating_sub_then_saturate(lhs: Duration, rhs: Duration) {
1903 let expected =
1904 if lhs.checked_sub(rhs.neg()).is_none() && lhs.is_negative() && rhs.neg().is_positive()
1905 {
1906 Duration::MIN
1907 } else {
1908 Duration::MAX
1909 };
1910 assert_eq!(lhs.saturating_sub(rhs.neg()), expected);
1911 }
1912
1913 #[rstest]
1914 #[case::zero(Duration::ZERO, 0)]
1915 #[case::positive_one(Duration::positive(1, 0), 1)]
1916 #[case::positive_one_nano(Duration::positive(0, 1), 0)]
1917 #[case::positive_one_sec_and_nano(Duration::positive(1, 1), 1)]
1918 #[case::negative_one(Duration::negative(1, 0), -1)]
1919 #[case::negative_one_nano(Duration::negative(0, 1), 0)]
1920 #[case::negative_one_sec_and_nano(Duration::negative(1, 1), -1)]
1921 #[case::some_positive_value(Duration::positive(123_456_789, 987_654_321), 123_456_789)]
1922 #[case::some_negative_value(Duration::negative(123_456_789, 987_654_321), -123_456_789)]
1923 #[case::min(Duration::MIN, i128::from(u64::MAX).neg())]
1924 #[case::max(Duration::MAX, i128::from(u64::MAX))]
1925 fn test_fundu_duration_as_seconds(#[case] duration: Duration, #[case] expected: i128) {
1926 assert_eq!(duration.as_seconds(), expected);
1927 }
1928
1929 #[rstest]
1930 #[case::zero(Duration::ZERO)]
1931 #[case::one(Duration::positive(1, 0))]
1932 #[case::one_nano(Duration::positive(0, 1))]
1933 #[case::one_sec_and_nano(Duration::positive(1, 1))]
1934 #[case::one_minute(Duration::positive(60, 0))]
1935 #[case::one_minute_plus_one_sec(Duration::positive(61, 0))]
1936 #[case::one_minute_minus_one_sec(Duration::positive(59, 0))]
1937 #[case::two_minutes(Duration::positive(120, 0))]
1938 #[case::some_value(Duration::positive(123_456_789, 987_654_321))]
1939 #[case::min(Duration::MIN)]
1940 #[case::max(Duration::MAX)]
1941 fn test_fundu_duration_as_whole(
1942 #[case] duration: Duration,
1943 #[values(2, 3, 4, 60, u64::MAX / 2, u64::MAX)] factor: u64,
1944 ) {
1945 let mut expected = i64::try_from(duration.inner.as_secs() / factor).unwrap();
1946 if duration.is_negative() {
1947 expected = expected.neg();
1948 }
1949 assert_eq!(duration.as_whole(factor), expected);
1950 assert_eq!(duration.neg().as_whole(factor), expected.neg());
1951 }
1952
1953 #[rstest]
1954 #[case::one_week(86400 * 7, 1)]
1955 #[case::one_week_plus_sec(86400 * 7 + 1, 1)]
1956 #[case::one_week_minus_sec(86400 * 7 - 1, 0)]
1957 fn test_fundu_duration_as_weeks(#[case] seconds: u64, #[case] expected: i64) {
1958 let duration = Duration::positive(seconds, 0);
1959
1960 assert_eq!(duration.as_weeks(), expected);
1961 assert_eq!(duration.neg().as_weeks(), expected.neg());
1962 }
1963
1964 #[rstest]
1965 #[case::one_day(86400, 1)]
1966 #[case::one_day_plus_sec(86400 + 1, 1)]
1967 #[case::one_day_minus_sec(86400 - 1, 0)]
1968 fn test_fundu_duration_as_days(#[case] seconds: u64, #[case] expected: i64) {
1969 let duration = Duration::positive(seconds, 0);
1970
1971 assert_eq!(duration.as_days(), expected);
1972 assert_eq!(duration.neg().as_days(), expected.neg());
1973 }
1974
1975 #[rstest]
1976 #[case::one_hour(3600, 1)]
1977 #[case::one_hour_plus_sec(3600 + 1, 1)]
1978 #[case::one_hour_minus_sec(3600 - 1, 0)]
1979 fn test_fundu_duration_as_hours(#[case] seconds: u64, #[case] expected: i64) {
1980 let duration = Duration::positive(seconds, 0);
1981
1982 assert_eq!(duration.as_hours(), expected);
1983 assert_eq!(duration.neg().as_hours(), expected.neg());
1984 }
1985
1986 #[rstest]
1987 #[case::one_minute(60, 1)]
1988 #[case::one_minute_plus_sec(60 + 1, 1)]
1989 #[case::one_minute_minus_sec(60 - 1, 0)]
1990 fn test_fundu_duration_as_minutes(#[case] seconds: u64, #[case] expected: i64) {
1991 let duration = Duration::positive(seconds, 0);
1992
1993 assert_eq!(duration.as_minutes(), expected);
1994 assert_eq!(duration.neg().as_minutes(), expected.neg());
1995 }
1996
1997 #[template]
1998 #[rstest]
1999 #[case::zero(Duration::ZERO, 0)]
2000 #[case::one_second(Duration::positive(1, 0), 1_000_000_000)]
2001 #[case::one_second_and_one_nano(Duration::positive(1, 1), 1_000_000_001)]
2002 #[case::one_second_and_one_nano(Duration::positive(1, 1), 1_000_000_001)]
2003 #[case::two_second(Duration::positive(2, 0), 2_000_000_000)]
2004 #[case::two_second_and_one_nano(Duration::positive(2, 1), 2_000_000_001)]
2005 #[case::one_sec_and_one_micro(Duration::positive(2, 1_000), 2_000_001_000)]
2006 #[case::one_sec_and_one_milli(Duration::positive(2, 1_000_000), 2_001_000_000)]
2007 #[case::some_value(Duration::positive(123_456_789, 987_654_321), 123_456_789_987_654_321)]
2008 #[case::max(Duration::MAX, i128::from(u64::MAX) * 1_000_000_000 + 999_999_999)]
2009 #[case::min(Duration::MIN, i128::from(u64::MAX).neg() * 1_000_000_000 - 999_999_999)]
2010 fn test_fundu_duration_as_sub_sec_template(#[case] duration: Duration, #[case] expected: i128) {
2011 }
2012
2013 #[apply(test_fundu_duration_as_sub_sec_template)]
2014 fn test_fundu_duration_as_nanos(duration: Duration, expected: i128) {
2015 assert_eq!(duration.as_nanos(), expected);
2016 assert_eq!(duration.neg().as_nanos(), expected.neg());
2017 }
2018
2019 #[apply(test_fundu_duration_as_sub_sec_template)]
2020 fn test_fundu_duration_as_micros(duration: Duration, expected: i128) {
2021 assert_eq!(duration.as_micros(), expected / 1_000);
2022 assert_eq!(duration.neg().as_micros(), expected.neg() / 1_000);
2023 }
2024
2025 #[apply(test_fundu_duration_as_sub_sec_template)]
2026 fn test_fundu_duration_as_millis(duration: Duration, expected: i128) {
2027 assert_eq!(duration.as_millis(), expected / 1_000_000);
2028 assert_eq!(duration.neg().as_millis(), expected.neg() / 1_000_000);
2029 }
2030
2031 #[template]
2032 #[rstest]
2033 #[case::zero(Duration::ZERO, 0i32)]
2034 #[case::one_sec(Duration::positive(1, 0), 0i32)]
2035 #[case::one_sec_and_one_nano(Duration::positive(1, 1), 1i32)]
2036 #[case::two_nano(Duration::positive(0, 2), 2i32)]
2037 #[case::one_micro(Duration::positive(0, 1_000), 1_000i32)]
2038 #[case::one_milli(Duration::positive(0, 1_000_000), 1_000_000i32)]
2039 #[case::some_value(Duration::positive(0, 123_456_789), 123_456_789i32)]
2040 #[case::max(Duration::MAX, 999_999_999i32)]
2041 #[case::min(Duration::MIN, -999_999_999i32)]
2042 fn test_fundu_duration_subsec_template(#[case] duration: Duration, #[case] expected: i32) {}
2043
2044 #[apply(test_fundu_duration_subsec_template)]
2045 fn test_fundu_duration_subsec_nanos(duration: Duration, expected: i32) {
2046 assert_eq!(duration.subsec_nanos(), expected);
2047 assert_eq!(duration.neg().subsec_nanos(), expected.neg());
2048 }
2049
2050 #[apply(test_fundu_duration_subsec_template)]
2051 fn test_fundu_duration_subsec_micros(duration: Duration, expected: i32) {
2052 let expected = expected / 1000i32;
2053 assert_eq!(duration.subsec_micros(), expected);
2054 assert_eq!(duration.neg().subsec_micros(), expected.neg());
2055 }
2056
2057 #[apply(test_fundu_duration_subsec_template)]
2058 fn test_fundu_duration_subsec_millis(duration: Duration, expected: i32) {
2059 let expected = expected / 1_000_000i32;
2060 assert_eq!(duration.subsec_millis(), expected);
2061 assert_eq!(duration.neg().subsec_millis(), expected.neg());
2062 }
2063
2064 #[rstest]
2065 #[case::zero(Duration::ZERO)]
2066 #[case::one(Duration::positive(1, 0))]
2067 #[case::one_plus_one_nano(Duration::positive(1, 1))]
2068 #[case::two(Duration::positive(2, 0))]
2069 #[case::two_plus_one_nano(Duration::positive(2, 1))]
2070 #[case::sixty(Duration::positive(60, 0))]
2071 #[case::sixty_plus_one(Duration::positive(61, 0))]
2072 #[case::sixty_minus_one(Duration::positive(59, 0))]
2073 #[case::some_value(Duration::positive(123_456_789, 987_654_321))]
2074 #[case::max(Duration::MAX)]
2075 #[case::min(Duration::MIN)]
2076 fn test_fundu_duration_extract_i64(
2077 #[case] mut duration: Duration,
2078 #[values(2, 3, 4, 5, 60, u64::MAX / 2, u64::MAX)] factor: u64,
2079 ) {
2080 let mut expected_number = i64::try_from(duration.inner.as_secs() / factor).unwrap();
2081 if duration.is_negative() {
2082 expected_number = expected_number.neg();
2083 }
2084 let expected_duration = Duration::from_std(
2085 duration.is_negative(),
2086 StdDuration::new(
2087 duration.inner.as_secs() % factor,
2088 duration.inner.subsec_nanos(),
2089 ),
2090 );
2091
2092 let actual_number = duration.extract_i64(factor);
2093 assert_eq!(actual_number, expected_number);
2094 assert_eq!(duration, expected_duration);
2095 }
2096
2097 #[rstest]
2098 #[case::one(Duration::positive(86400 * 7, 123), 1, Duration::positive(0, 123))]
2099 #[case::one_plus_sec(
2100 Duration::positive(86400 * 7 + 1, 123),
2101 1,
2102 Duration::positive(1, 123)
2103 )]
2104 #[case::one_minus_sec(
2105 Duration::positive(86400 * 7 - 1, 123),
2106 0,
2107 Duration::positive(86400 * 7 - 1, 123)
2108 )]
2109 fn test_fundu_duration_extract_weeks(
2110 #[case] duration: Duration,
2111 #[case] expected_number: i64,
2112 #[case] expected_duration: Duration,
2113 ) {
2114 let mut duration = duration;
2115 let number = duration.extract_weeks();
2116 assert_eq!(number, expected_number);
2117 assert_eq!(duration, expected_duration);
2118 }
2119
2120 #[rstest]
2121 #[case::one(Duration::positive(86400, 123), 1, Duration::positive(0, 123))]
2122 #[case::one_plus_sec(
2123 Duration::positive(86400 + 1, 123),
2124 1,
2125 Duration::positive(1, 123)
2126 )]
2127 #[case::one_minus_sec(
2128 Duration::positive(86400 - 1, 123),
2129 0,
2130 Duration::positive(86400 - 1, 123)
2131 )]
2132 fn test_fundu_duration_extract_days(
2133 #[case] duration: Duration,
2134 #[case] expected_number: i64,
2135 #[case] expected_duration: Duration,
2136 ) {
2137 let mut duration = duration;
2138 let number = duration.extract_days();
2139 assert_eq!(number, expected_number);
2140 assert_eq!(duration, expected_duration);
2141 }
2142
2143 #[rstest]
2144 #[case::one(Duration::positive(3600, 123), 1, Duration::positive(0, 123))]
2145 #[case::one_plus_sec(Duration::positive(3600 + 1, 123), 1, Duration::positive(1, 123))]
2146 #[case::one_minus_sec(Duration::positive(3600 - 1, 123), 0, Duration::positive(3599, 123))]
2147 fn test_fundu_duration_extract_hours(
2148 #[case] duration: Duration,
2149 #[case] expected_number: i64,
2150 #[case] expected_duration: Duration,
2151 ) {
2152 let mut duration = duration;
2153 let number = duration.extract_hours();
2154 assert_eq!(number, expected_number);
2155 assert_eq!(duration, expected_duration);
2156 }
2157
2158 #[rstest]
2159 #[case::one(Duration::positive(60, 123), 1, Duration::positive(0, 123))]
2160 #[case::one_plus_sec(Duration::positive(60 + 1, 123), 1, Duration::positive(1, 123))]
2161 #[case::one_minus_sec(Duration::positive(60 - 1, 123), 0, Duration::positive(59, 123))]
2162 fn test_fundu_duration_extract_minutes(
2163 #[case] duration: Duration,
2164 #[case] expected_number: i64,
2165 #[case] expected_duration: Duration,
2166 ) {
2167 let mut duration = duration;
2168 let number = duration.extract_minutes();
2169 assert_eq!(number, expected_number);
2170 assert_eq!(duration, expected_duration);
2171 }
2172
2173 #[rstest]
2174 #[case::zero(Duration::ZERO, 0, Duration::ZERO)]
2175 #[case::one(Duration::positive(1, 123), 1, Duration::positive(0, 123))]
2176 #[case::two(Duration::positive(2, 123), 2, Duration::positive(0, 123))]
2177 #[case::zero_sec(Duration::positive(0, 123), 0, Duration::positive(0, 123))]
2178 #[case::max(
2179 Duration::MAX,
2180 i128::from(u64::MAX),
2181 Duration::positive(0, 999_999_999)
2182 )]
2183 #[case::min(Duration::MIN, i128::from(u64::MAX).neg(), Duration::negative(0, 999_999_999))]
2184 fn test_fundu_duration_extract_seconds(
2185 #[case] duration: Duration,
2186 #[case] expected_number: i128,
2187 #[case] expected_duration: Duration,
2188 ) {
2189 let mut duration_copy = duration;
2190 let number = duration_copy.extract_seconds();
2191 assert_eq!(number, expected_number);
2192 assert_eq!(duration_copy, expected_duration);
2193
2194 let mut duration = duration.neg();
2195 let number = duration.extract_seconds();
2196 assert_eq!(number, expected_number.neg());
2197 assert_eq!(duration, expected_duration.neg());
2198 }
2199
2200 #[rstest]
2201 #[case::positive_zero(Duration::ZERO, Duration::ZERO, Ordering::Equal)]
2202 #[case::negative_zero(Duration::negative(0, 0), Duration::negative(0, 0), Ordering::Equal)]
2203 #[case::negative_zero_and_positive_zero(
2204 Duration::negative(0, 0),
2205 Duration::ZERO,
2206 Ordering::Equal
2207 )]
2208 #[case::both_positive_one_sec(
2209 Duration::positive(1, 0),
2210 Duration::positive(1, 0),
2211 Ordering::Equal
2212 )]
2213 #[case::both_negative_one_sec(
2214 Duration::negative(1, 0),
2215 Duration::negative(1, 0),
2216 Ordering::Equal
2217 )]
2218 #[case::negative_and_positive_one_sec(
2219 Duration::negative(1, 0),
2220 Duration::positive(1, 0),
2221 Ordering::Less
2222 )]
2223 #[case::one_nano_second_difference_positive(
2224 Duration::ZERO,
2225 Duration::positive(0, 1),
2226 Ordering::Less
2227 )]
2228 #[case::one_nano_second_difference_negative(
2229 Duration::ZERO,
2230 Duration::negative(0, 1),
2231 Ordering::Greater
2232 )]
2233 #[case::max(Duration::MAX, Duration::MAX, Ordering::Equal)]
2234 #[case::one_nano_below_max(
2235 Duration::MAX,
2236 Duration::positive(u64::MAX, 999_999_998),
2237 Ordering::Greater
2238 )]
2239 #[case::min(Duration::MIN, Duration::MIN, Ordering::Equal)]
2240 #[case::one_nano_above_min(
2241 Duration::MIN,
2242 Duration::negative(u64::MAX, 999_999_998),
2243 Ordering::Less
2244 )]
2245 #[case::min_max(Duration::MIN, Duration::MAX, Ordering::Less)]
2246 #[case::mixed_positive(
2247 Duration::positive(123, 987),
2248 Duration::positive(987, 123),
2249 Ordering::Less
2250 )]
2251 #[case::mixed_negative(
2252 Duration::negative(123, 987),
2253 Duration::negative(987, 123),
2254 Ordering::Greater
2255 )]
2256 #[case::mixed_positive_and_negative(
2257 Duration::positive(123, 987),
2258 Duration::negative(987, 123),
2259 Ordering::Greater
2260 )]
2261 fn test_duration_partial_and_total_ordering(
2262 #[case] lhs: Duration,
2263 #[case] rhs: Duration,
2264 #[case] expected: Ordering,
2265 ) {
2266 assert_eq!(lhs.partial_cmp(&rhs), Some(expected));
2267 assert_eq!(rhs.partial_cmp(&lhs), Some(expected.reverse()));
2268 }
2269
2270 #[rstest]
2271 #[case::positive_zero(Duration::ZERO, Duration::ZERO)]
2272 #[case::negative_zero(Duration::negative(0, 0), Duration::negative(0, 0))]
2273 #[case::negative_zero_and_positive_zero(Duration::negative(0, 0), Duration::ZERO)]
2274 #[case::positive_one_sec(Duration::positive(1, 0), Duration::positive(1, 0))]
2275 #[case::negative_one_sec(Duration::negative(1, 0), Duration::negative(1, 0))]
2276 #[case::max(Duration::MAX, Duration::MAX)]
2277 #[case::min(Duration::MIN, Duration::MIN)]
2278 fn test_duration_hash_and_eq_property_when_equal(
2279 #[case] duration: Duration,
2280 #[case] other: Duration,
2281 ) {
2282 assert_eq!(duration, other);
2283 assert_eq!(other, duration);
2284
2285 let mut hasher = DefaultHasher::new();
2286 duration.hash(&mut hasher);
2287
2288 let mut other_hasher = DefaultHasher::new();
2289 other.hash(&mut other_hasher);
2290
2291 assert_eq!(hasher.finish(), other_hasher.finish());
2292 }
2293
2294 #[rstest]
2295 #[case::zero_and_positive_one(Duration::ZERO, Duration::positive(1, 0))]
2296 #[case::zero_and_negative_one(Duration::ZERO, Duration::negative(1, 0))]
2297 #[case::positive_sec(Duration::positive(1, 0), Duration::negative(2, 0))]
2298 #[case::positive_and_negative_one_sec(Duration::positive(1, 0), Duration::negative(1, 0))]
2299 #[case::min_and_max(Duration::MIN, Duration::MAX)]
2300 fn test_duration_hash_and_eq_property_when_not_equal(
2301 #[case] duration: Duration,
2302 #[case] other: Duration,
2303 ) {
2304 assert_ne!(duration, other);
2305 assert_ne!(other, duration);
2306
2307 let mut hasher = DefaultHasher::new();
2308 duration.hash(&mut hasher);
2309
2310 let mut other_hasher = DefaultHasher::new();
2311 other.hash(&mut other_hasher);
2312
2313 assert_ne!(hasher.finish(), other_hasher.finish());
2314 }
2315
2316 #[rstest]
2317 #[case::positive_zero(Duration::ZERO, StdDuration::ZERO)]
2318 #[case::negative_zero(Duration::negative(0, 0), StdDuration::ZERO)]
2319 #[case::positive_one(Duration::positive(1, 0), StdDuration::new(1, 0))]
2320 #[case::positive_one_nano(Duration::positive(0, 1), StdDuration::new(0, 1))]
2321 #[case::negative_one(Duration::negative(1, 0), StdDuration::ZERO)]
2322 #[case::negative_one_nano(Duration::negative(0, 1), StdDuration::ZERO)]
2323 #[case::max(Duration::MAX, StdDuration::MAX)]
2324 fn test_fundu_duration_saturating_into_std_duration(
2325 #[case] duration: Duration,
2326 #[case] expected: StdDuration,
2327 ) {
2328 assert_eq!(
2329 SaturatingInto::<std::time::Duration>::saturating_into(duration),
2330 expected
2331 );
2332 }
2333
2334 #[cfg(feature = "time")]
2335 #[rstest]
2336 #[case::positive_zero(Duration::ZERO, time::Duration::ZERO)]
2337 #[case::negative_zero(Duration::negative(0, 0), time::Duration::ZERO)]
2338 #[case::positive_one(Duration::positive(1, 0), time::Duration::new(1, 0))]
2339 #[case::negative_one(Duration::negative(1, 0), time::Duration::new(-1, 0))]
2340 #[case::negative_barely_no_overflow(
2341 Duration::negative(i64::MIN.unsigned_abs(), 999_999_999),
2342 time::Duration::MIN
2343 )]
2344 #[case::negative_barely_overflow(
2345 Duration::negative(i64::MIN.unsigned_abs() + 1, 0),
2346 time::Duration::MIN
2347 )]
2348 #[case::negative_max_overflow(Duration::negative(u64::MAX, 999_999_999), time::Duration::MIN)]
2349 #[case::positive_barely_no_overflow(
2350 Duration::positive(i64::MAX as u64, 999_999_999),
2351 time::Duration::MAX
2352 )]
2353 #[case::positive_barely_overflow(
2354 Duration::positive(i64::MAX as u64 + 1, 999_999_999),
2355 time::Duration::MAX
2356 )]
2357 #[case::positive_max_overflow(Duration::positive(u64::MAX, 999_999_999), time::Duration::MAX)]
2358 fn test_fundu_duration_saturating_into_time_duration(
2359 #[case] duration: Duration,
2360 #[case] expected: time::Duration,
2361 ) {
2362 assert_eq!(
2363 SaturatingInto::<time::Duration>::saturating_into(duration),
2364 expected
2365 );
2366 }
2367
2368 #[cfg(feature = "chrono")]
2369 #[rstest]
2370 #[case::positive_zero(Duration::ZERO, chrono::Duration::zero())]
2371 #[case::negative_zero(Duration::negative(0, 0), chrono::Duration::zero())]
2372 #[case::positive_one(Duration::positive(1, 0), chrono::Duration::seconds(1))]
2373 #[case::negative_one(Duration::negative(1, 0), chrono::Duration::seconds(-1))]
2374 #[case::negative_barely_no_overflow(
2375 Duration::negative(i64::MIN.unsigned_abs() / 1000, 808_000_000),
2376 chrono::TimeDelta::MIN
2377 )]
2378 #[case::negative_barely_overflow(
2379 Duration::negative(i64::MIN.unsigned_abs() / 1000 + 1, 0),
2380 chrono::TimeDelta::MIN
2381 )]
2382 #[case::negative_max_overflow(
2383 Duration::negative(u64::MAX, 999_999_999),
2384 chrono::TimeDelta::MIN
2385 )]
2386 #[case::positive_barely_no_overflow(
2387 Duration::positive(i64::MAX as u64 / 1000, 807_000_000),
2388 chrono::TimeDelta::MAX
2389 )]
2390 #[case::positive_barely_overflow(
2391 Duration::positive(i64::MAX as u64 / 1000 + 1, 807_000_000),
2392 chrono::TimeDelta::MAX
2393 )]
2394 #[case::positive_max_overflow(
2395 Duration::positive(u64::MAX, 999_999_999),
2396 chrono::TimeDelta::MAX
2397 )]
2398 fn test_fundu_duration_saturating_into_chrono_duration(
2399 #[case] duration: Duration,
2400 #[case] expected: chrono::Duration,
2401 ) {
2402 assert_eq!(
2403 SaturatingInto::<chrono::Duration>::saturating_into(duration),
2404 expected
2405 );
2406 }
2407
2408 #[rstest]
2409 #[case::negative_one(Duration::negative(1, 0))]
2410 #[case::negative_one_nano(Duration::negative(0, 1))]
2411 #[case::one_one(Duration::negative(1, 1))]
2412 #[case::min(Duration::MIN)]
2413 fn test_std_duration_try_from_for_fundu_duration_then_error(#[case] duration: Duration) {
2414 assert_eq!(
2415 TryInto::<std::time::Duration>::try_into(duration).unwrap_err(),
2416 TryFromDurationError::NegativeDuration
2417 );
2418 }
2419
2420 #[cfg(feature = "chrono")]
2421 #[rstest]
2422 #[case::zero(Duration::ZERO, ChronoDuration::zero())]
2423 #[case::negative_zero(Duration::negative(0, 0), ChronoDuration::zero())]
2424 #[case::positive_one_sec(Duration::positive(1, 0), ChronoDuration::seconds(1))]
2425 #[case::positive_one_sec_and_nano(
2426 Duration::positive(1, 1),
2427 ChronoDuration::nanoseconds(1_000_000_001)
2428 )]
2429 #[case::negative_one_sec(Duration::negative(1, 0), ChronoDuration::seconds(-1))]
2430 #[case::negative_one_sec_and_nano(
2431 Duration::negative(1, 1),
2432 ChronoDuration::nanoseconds(-1_000_000_001)
2433 )]
2434 #[case::max_nanos(
2435 Duration::positive(0, 999_999_999),
2436 ChronoDuration::nanoseconds(999_999_999)
2437 )]
2438 #[case::min_nanos(
2439 Duration::negative(0, 999_999_999),
2440 ChronoDuration::nanoseconds(-999_999_999)
2441 )]
2442 #[case::max_secs(
2443 Duration::positive(i64::MAX as u64 / 1000, 0),
2444 ChronoDuration::seconds(i64::MAX / 1000))
2445 ]
2446 #[case::max_secs_and_nanos(
2447 Duration::positive(i64::MAX as u64 / 1000, 807_000_000),
2448 chrono::TimeDelta::MAX)
2449 ]
2450 #[case::secs_and_nanos_one_below_max(
2451 Duration::positive(i64::MAX as u64 / 1000, 807_000_000 - 1),
2452 chrono::TimeDelta::MAX.checked_sub(&ChronoDuration::nanoseconds(1)).unwrap())
2453 ]
2454 #[case::min_secs(
2455 Duration::negative(i64::MIN.unsigned_abs() / 1000, 0),
2456 ChronoDuration::seconds(i64::MIN / 1000))
2457 ]
2458 #[case::min_secs_and_nanos(
2459 Duration::negative(i64::MIN.unsigned_abs() / 1000, 807_000_000),
2460 chrono::TimeDelta::MIN)
2461 ]
2462 #[case::secs_and_nanos_one_above_min(
2463 Duration::negative(i64::MIN.unsigned_abs() / 1000, 807_000_000 - 1),
2464 chrono::TimeDelta::MIN.checked_add(&ChronoDuration::nanoseconds(1)).unwrap())
2465 ]
2466 fn test_chrono_duration_try_from_fundu_duration(
2467 #[case] duration: Duration,
2468 #[case] expected: ChronoDuration,
2469 ) {
2470 let chrono_duration: ChronoDuration = duration.try_into().unwrap();
2471 assert_eq!(chrono_duration, expected);
2472 }
2473
2474 #[cfg(feature = "chrono")]
2475 #[rstest]
2476 #[case::positive_overflow_secs(
2477 Duration::positive(i64::MAX as u64 / 1000 + 1, 0),
2478 TryFromDurationError::PositiveOverflow)
2479 ]
2480 #[case::positive_overflow_secs_and_nanos(
2481 Duration::positive(i64::MAX as u64 / 1000, 807_000_000 + 1),
2482 TryFromDurationError::PositiveOverflow)
2483 ]
2484 #[case::positive_overflow_max_fundu_duration(
2485 Duration::MAX,
2486 TryFromDurationError::PositiveOverflow
2487 )]
2488 #[case::negative_overflow_secs(
2489 Duration::negative(i64::MIN.unsigned_abs() / 1000 + 1, 0),
2490 TryFromDurationError::NegativeOverflow)
2491 ]
2492 #[case::negative_overflow_secs_and_nanos(
2493 Duration::negative(i64::MIN.unsigned_abs() / 1000, 808_000_001),
2494 TryFromDurationError::NegativeOverflow)
2495 ]
2496 #[case::negative_overflow_min_fundu_duration(
2497 Duration::MIN,
2498 TryFromDurationError::NegativeOverflow
2499 )]
2500 fn test_chrono_duration_try_from_fundu_duration_then_error(
2501 #[case] duration: Duration,
2502 #[case] expected: TryFromDurationError,
2503 ) {
2504 let result: Result<ChronoDuration, TryFromDurationError> = duration.try_into();
2505 assert_eq!(result.unwrap_err(), expected);
2506 }
2507
2508 #[cfg(feature = "time")]
2509 #[test]
2510 fn test_time_duration_try_from_fundu_duration() {
2511 let duration = Duration::from_std(false, std::time::Duration::new(1, 0));
2512 let time_duration: time::Duration = duration.try_into().unwrap();
2513 assert_eq!(time_duration, time::Duration::new(1, 0));
2514 }
2515
2516 #[rstest]
2517 #[case::zero(
2518 std::time::Duration::ZERO,
2519 Duration::from_std(false, std::time::Duration::ZERO)
2520 )]
2521 #[case::one(
2522 std::time::Duration::new(1, 0),
2523 Duration::from_std(false, std::time::Duration::new(1, 0))
2524 )]
2525 #[case::with_nano_seconds(
2526 std::time::Duration::new(1, 123_456_789),
2527 Duration::from_std(false, std::time::Duration::new(1, 123_456_789))
2528 )]
2529 #[case::max(
2530 std::time::Duration::MAX,
2531 Duration::from_std(false, std::time::Duration::MAX)
2532 )]
2533 fn test_fundu_duration_from_std_time_duration(
2534 #[case] std_duration: std::time::Duration,
2535 #[case] expected: Duration,
2536 ) {
2537 assert_eq!(Duration::from(std_duration), expected);
2538 }
2539
2540 #[cfg(feature = "time")]
2541 #[rstest]
2542 #[case::zero(time::Duration::ZERO, Duration::ZERO)]
2543 #[case::positive_one(time::Duration::new(1, 0), Duration::positive(1, 0))]
2544 #[case::positive_one_nano(time::Duration::new(0, 1), Duration::positive(0, 1))]
2545 #[case::negative_one(time::Duration::new(-1, 0), Duration::negative(1, 0))]
2546 #[case::negative_one_nano(time::Duration::new(0, -1), Duration::negative(0, 1))]
2547 #[case::positive_one_negative_one_nano(
2548 time::Duration::new(1, -1),
2549 Duration::positive(0, 999_999_999)
2550 )]
2551 #[case::negative_one_positive_one_nano(
2552 time::Duration::new(-1, 1),
2553 Duration::negative(0, 999_999_999)
2554 )]
2555 #[case::min(time::Duration::MIN, Duration::negative(i64::MIN.unsigned_abs(), 999_999_999))]
2556 #[case::max(time::Duration::MAX, Duration::positive(i64::MAX as u64, 999_999_999))]
2557 fn test_fundu_duration_from_time_duration(
2558 #[case] time_duration: time::Duration,
2559 #[case] expected: Duration,
2560 ) {
2561 assert_eq!(Duration::from(time_duration), expected);
2562 }
2563
2564 #[cfg(feature = "chrono")]
2565 #[rstest]
2566 #[case::zero(chrono::Duration::zero(), Duration::ZERO)]
2567 #[case::positive_one(chrono::Duration::seconds(1), Duration::positive(1, 0))]
2568 #[case::positive_one_nano(chrono::Duration::nanoseconds(1), Duration::positive(0, 1))]
2569 #[case::negative_one(chrono::Duration::seconds(-1), Duration::negative(1, 0))]
2570 #[case::negative_one_nano(chrono::Duration::nanoseconds(-1), Duration::negative(0, 1))]
2571 #[case::min(chrono::TimeDelta::MIN, CHRONO_MIN_DURATION)]
2572 #[case::max(chrono::TimeDelta::MAX, CHRONO_MAX_DURATION)]
2573 fn test_fundu_duration_from_chrono_duration(
2574 #[case] chrono_duration: chrono::Duration,
2575 #[case] expected: Duration,
2576 ) {
2577 assert_eq!(Duration::from(chrono_duration), expected);
2578 }
2579}