1use crate::{
2 fmt::{
3 util::{DecimalFormatter, FractionalFormatter},
4 Write, WriteExt,
5 },
6 Error, SignedDuration, Span, Unit,
7};
8
9const SECS_PER_HOUR: i64 = MINS_PER_HOUR * SECS_PER_MIN;
10const SECS_PER_MIN: i64 = 60;
11const MINS_PER_HOUR: i64 = 60;
12const NANOS_PER_HOUR: i128 =
13 (SECS_PER_MIN * MINS_PER_HOUR * NANOS_PER_SEC) as i128;
14const NANOS_PER_MIN: i128 = (SECS_PER_MIN * NANOS_PER_SEC) as i128;
15const NANOS_PER_SEC: i64 = 1_000_000_000;
16const NANOS_PER_MILLI: i32 = 1_000_000;
17const NANOS_PER_MICRO: i32 = 1_000;
18
19#[derive(Clone, Copy, Debug)]
45#[non_exhaustive]
46pub enum Designator {
47 Verbose,
50 Short,
53 Compact,
58 HumanTime,
85}
86
87#[derive(Clone, Copy, Debug)]
130#[non_exhaustive]
131pub enum Spacing {
132 None,
139 BetweenUnits,
144 BetweenUnitsAndDesignators,
150}
151
152impl Spacing {
153 fn between_units(self) -> &'static str {
154 match self {
155 Spacing::None => "",
156 Spacing::BetweenUnits => " ",
157 Spacing::BetweenUnitsAndDesignators => " ",
158 }
159 }
160
161 fn between_units_and_designators(self) -> &'static str {
162 match self {
163 Spacing::None => "",
164 Spacing::BetweenUnits => "",
165 Spacing::BetweenUnitsAndDesignators => " ",
166 }
167 }
168}
169
170#[derive(Clone, Copy, Debug)]
193#[non_exhaustive]
194pub enum Direction {
195 Auto,
207 Sign,
211 ForceSign,
215 Suffix,
219}
220
221impl Direction {
222 fn sign(
228 self,
229 printer: &SpanPrinter,
230 has_calendar: bool,
231 signum: i8,
232 ) -> Option<DirectionSign> {
233 match self {
234 Direction::Auto => match printer.spacing {
235 Spacing::None => {
236 if signum < 0 {
237 Some(DirectionSign::Prefix("-"))
238 } else {
239 None
240 }
241 }
242 Spacing::BetweenUnits
243 | Spacing::BetweenUnitsAndDesignators => {
244 if signum < 0 {
245 if printer.hms && !has_calendar {
246 Some(DirectionSign::Prefix("-"))
247 } else {
248 Some(DirectionSign::Suffix(" ago"))
249 }
250 } else {
251 None
252 }
253 }
254 },
255 Direction::Sign => {
256 if signum < 0 {
257 Some(DirectionSign::Prefix("-"))
258 } else {
259 None
260 }
261 }
262 Direction::ForceSign => {
263 Some(DirectionSign::Prefix(if signum < 0 { "-" } else { "+" }))
264 }
265 Direction::Suffix => {
266 if signum < 0 {
267 Some(DirectionSign::Suffix(" ago"))
268 } else {
269 None
270 }
271 }
272 }
273 }
274}
275
276#[derive(Clone, Copy, Debug)]
278enum DirectionSign {
279 Prefix(&'static str),
280 Suffix(&'static str),
281}
282
283#[derive(Clone, Copy, Debug)]
317#[non_exhaustive]
318pub enum FractionalUnit {
319 Hour,
324 Minute,
329 Second,
331 Millisecond,
333 Microsecond,
335}
336
337impl From<FractionalUnit> for Unit {
338 fn from(u: FractionalUnit) -> Unit {
339 match u {
340 FractionalUnit::Hour => Unit::Hour,
341 FractionalUnit::Minute => Unit::Minute,
342 FractionalUnit::Second => Unit::Second,
343 FractionalUnit::Millisecond => Unit::Millisecond,
344 FractionalUnit::Microsecond => Unit::Microsecond,
345 }
346 }
347}
348
349#[derive(Clone, Debug)]
456pub struct SpanPrinter {
457 designator: Designator,
458 spacing: Spacing,
459 direction: Direction,
460 fractional: Option<FractionalUnit>,
461 comma_after_designator: bool,
462 hms: bool,
463 padding: Option<u8>,
464 precision: Option<u8>,
465 zero_unit: Unit,
466}
467
468impl SpanPrinter {
469 #[inline]
491 pub const fn new() -> SpanPrinter {
492 SpanPrinter {
493 designator: Designator::Compact,
494 spacing: Spacing::BetweenUnits,
495 direction: Direction::Auto,
496 fractional: None,
497 comma_after_designator: false,
498 hms: false,
499 padding: None,
500 precision: None,
501 zero_unit: Unit::Second,
502 }
503 }
504
505 #[inline]
534 pub const fn designator(self, designator: Designator) -> SpanPrinter {
535 SpanPrinter { designator, ..self }
536 }
537
538 #[inline]
598 pub const fn spacing(self, spacing: Spacing) -> SpanPrinter {
599 SpanPrinter { spacing, ..self }
600 }
601
602 #[inline]
625 pub const fn direction(self, direction: Direction) -> SpanPrinter {
626 SpanPrinter { direction, ..self }
627 }
628
629 #[inline]
679 pub const fn fractional(
680 self,
681 unit: Option<FractionalUnit>,
682 ) -> SpanPrinter {
683 SpanPrinter { fractional: unit, ..self }
684 }
685
686 #[inline]
707 pub const fn comma_after_designator(self, yes: bool) -> SpanPrinter {
708 SpanPrinter { comma_after_designator: yes, ..self }
709 }
710
711 #[inline]
810 pub const fn hours_minutes_seconds(self, yes: bool) -> SpanPrinter {
811 SpanPrinter { hms: yes, ..self }
812 }
813
814 #[inline]
855 pub const fn padding(self, digits: u8) -> SpanPrinter {
856 SpanPrinter { padding: Some(digits), ..self }
857 }
858
859 #[inline]
908 pub const fn precision(self, precision: Option<u8>) -> SpanPrinter {
909 SpanPrinter { precision, ..self }
910 }
911
912 #[inline]
967 pub const fn zero_unit(self, unit: Unit) -> SpanPrinter {
968 SpanPrinter { zero_unit: unit, ..self }
969 }
970
971 #[cfg(any(test, feature = "alloc"))]
987 pub fn span_to_string(&self, span: &Span) -> alloc::string::String {
988 let mut buf = alloc::string::String::with_capacity(4);
989 self.print_span(span, &mut buf).unwrap();
991 buf
992 }
993
994 #[cfg(any(test, feature = "alloc"))]
1028 pub fn duration_to_string(
1029 &self,
1030 duration: &SignedDuration,
1031 ) -> alloc::string::String {
1032 let mut buf = alloc::string::String::with_capacity(4);
1033 self.print_duration(duration, &mut buf).unwrap();
1035 buf
1036 }
1037
1038 pub fn print_span<W: Write>(
1062 &self,
1063 span: &Span,
1064 wtr: W,
1065 ) -> Result<(), Error> {
1066 if self.hms {
1067 return self.print_span_hms(span, wtr);
1068 }
1069 self.print_span_designators(span, wtr)
1070 }
1071
1072 pub fn print_duration<W: Write>(
1105 &self,
1106 duration: &SignedDuration,
1107 wtr: W,
1108 ) -> Result<(), Error> {
1109 if self.hms {
1110 return self.print_duration_hms(duration, wtr);
1111 }
1112 self.print_duration_designators(duration, wtr)
1113 }
1114
1115 fn print_span_designators<W: Write>(
1116 &self,
1117 span: &Span,
1118 mut wtr: W,
1119 ) -> Result<(), Error> {
1120 let mut wtr =
1121 DesignatorWriter::new(self, &mut wtr, false, span.signum());
1122 wtr.maybe_write_prefix_sign()?;
1123 match self.fractional {
1124 None => {
1125 self.print_span_designators_non_fraction(span, &mut wtr)?;
1126 }
1127 Some(unit) => {
1128 self.print_span_designators_fractional(span, unit, &mut wtr)?;
1129 }
1130 }
1131 wtr.maybe_write_zero()?;
1132 wtr.maybe_write_suffix_sign()?;
1133 Ok(())
1134 }
1135
1136 fn print_span_designators_non_fraction<'p, 'w, W: Write>(
1137 &self,
1138 span: &Span,
1139 wtr: &mut DesignatorWriter<'p, 'w, W>,
1140 ) -> Result<(), Error> {
1141 let span = span.abs();
1142 if span.get_years() != 0 {
1143 wtr.write(Unit::Year, span.get_years())?;
1144 }
1145 if span.get_months() != 0 {
1146 wtr.write(Unit::Month, span.get_months())?;
1147 }
1148 if span.get_weeks() != 0 {
1149 wtr.write(Unit::Week, span.get_weeks())?;
1150 }
1151 if span.get_days() != 0 {
1152 wtr.write(Unit::Day, span.get_days())?;
1153 }
1154 if span.get_hours() != 0 {
1155 wtr.write(Unit::Hour, span.get_hours())?;
1156 }
1157 if span.get_minutes() != 0 {
1158 wtr.write(Unit::Minute, span.get_minutes())?;
1159 }
1160 if span.get_seconds() != 0 {
1161 wtr.write(Unit::Second, span.get_seconds())?;
1162 }
1163 if span.get_milliseconds() != 0 {
1164 wtr.write(Unit::Millisecond, span.get_milliseconds())?;
1165 }
1166 if span.get_microseconds() != 0 {
1167 wtr.write(Unit::Microsecond, span.get_microseconds())?;
1168 }
1169 if span.get_nanoseconds() != 0 {
1170 wtr.write(Unit::Nanosecond, span.get_nanoseconds())?;
1171 }
1172 Ok(())
1173 }
1174
1175 #[inline(never)]
1176 fn print_span_designators_fractional<'p, 'w, W: Write>(
1177 &self,
1178 span: &Span,
1179 unit: FractionalUnit,
1180 wtr: &mut DesignatorWriter<'p, 'w, W>,
1181 ) -> Result<(), Error> {
1182 let split_at = Unit::from(unit).next().unwrap();
1185 let non_fractional = span.without_lower(split_at);
1186 let fractional = span.only_lower(split_at);
1187 self.print_span_designators_non_fraction(&non_fractional, wtr)?;
1188 wtr.write_fractional_duration(
1189 unit,
1190 &fractional.to_duration_invariant(),
1191 )?;
1192 Ok(())
1193 }
1194
1195 fn print_span_hms<W: Write>(
1196 &self,
1197 span: &Span,
1198 mut wtr: W,
1199 ) -> Result<(), Error> {
1200 let span_cal = span.only_calendar();
1201 let mut span_time = span.only_time();
1202 let has_cal = !span_cal.is_zero();
1203
1204 let mut wtr =
1205 DesignatorWriter::new(self, &mut wtr, has_cal, span.signum());
1206 wtr.maybe_write_prefix_sign()?;
1207 if has_cal {
1208 self.print_span_designators_non_fraction(&span_cal, &mut wtr)?;
1209 wtr.finish_preceding()?;
1210 if matches!(self.spacing, Spacing::None) {
1215 wtr.wtr.write_str(" ")?;
1216 }
1217 }
1218 span_time = span_time.abs();
1219
1220 let fmtint =
1221 DecimalFormatter::new().padding(self.padding.unwrap_or(2));
1222 let fmtfraction = FractionalFormatter::new().precision(self.precision);
1223 wtr.wtr.write_int(&fmtint, span_time.get_hours_ranged().get())?;
1224 wtr.wtr.write_str(":")?;
1225 wtr.wtr.write_int(&fmtint, span_time.get_minutes_ranged().get())?;
1226 wtr.wtr.write_str(":")?;
1227 let fp = FractionalPrinter::from_span(
1228 &span_time.only_lower(Unit::Minute),
1229 FractionalUnit::Second,
1230 fmtint,
1231 fmtfraction,
1232 );
1233 fp.print(&mut wtr.wtr)?;
1234 wtr.maybe_write_suffix_sign()?;
1235 Ok(())
1236 }
1237
1238 fn print_duration_designators<W: Write>(
1239 &self,
1240 dur: &SignedDuration,
1241 mut wtr: W,
1242 ) -> Result<(), Error> {
1243 let mut wtr =
1244 DesignatorWriter::new(self, &mut wtr, false, dur.signum());
1245 wtr.maybe_write_prefix_sign()?;
1246 match self.fractional {
1247 None => {
1248 let mut secs = dur.as_secs();
1249 wtr.write(Unit::Hour, (secs / SECS_PER_HOUR).abs())?;
1250 secs %= MINS_PER_HOUR * SECS_PER_MIN;
1251 wtr.write(Unit::Minute, (secs / SECS_PER_MIN).abs())?;
1252 wtr.write(Unit::Second, (secs % SECS_PER_MIN).abs())?;
1253 let mut nanos = dur.subsec_nanos();
1254 wtr.write(Unit::Millisecond, (nanos / NANOS_PER_MILLI).abs())?;
1255 nanos %= NANOS_PER_MILLI;
1256 wtr.write(Unit::Microsecond, (nanos / NANOS_PER_MICRO).abs())?;
1257 wtr.write(Unit::Nanosecond, (nanos % NANOS_PER_MICRO).abs())?;
1258 }
1259 Some(FractionalUnit::Hour) => {
1260 wtr.write_fractional_duration(FractionalUnit::Hour, dur)?;
1261 }
1262 Some(FractionalUnit::Minute) => {
1263 let mut secs = dur.as_secs();
1264 wtr.write(Unit::Hour, (secs / SECS_PER_HOUR).abs())?;
1265 secs %= MINS_PER_HOUR * SECS_PER_MIN;
1266
1267 let leftovers = SignedDuration::new(secs, dur.subsec_nanos());
1268 wtr.write_fractional_duration(
1269 FractionalUnit::Minute,
1270 &leftovers,
1271 )?;
1272 }
1273 Some(FractionalUnit::Second) => {
1274 let mut secs = dur.as_secs();
1275 wtr.write(Unit::Hour, (secs / SECS_PER_HOUR).abs())?;
1276 secs %= MINS_PER_HOUR * SECS_PER_MIN;
1277 wtr.write(Unit::Minute, (secs / SECS_PER_MIN).abs())?;
1278 secs %= SECS_PER_MIN;
1279
1280 let leftovers =
1283 SignedDuration::new(secs, dur.subsec_nanos()).abs();
1284 wtr.write_fractional_duration(
1285 FractionalUnit::Second,
1286 &leftovers,
1287 )?;
1288 }
1289 Some(FractionalUnit::Millisecond) => {
1290 let mut secs = dur.as_secs();
1291 wtr.write(Unit::Hour, (secs / SECS_PER_HOUR).abs())?;
1292 secs %= MINS_PER_HOUR * SECS_PER_MIN;
1293 wtr.write(Unit::Minute, (secs / SECS_PER_MIN).abs())?;
1294 wtr.write(Unit::Second, (secs % SECS_PER_MIN).abs())?;
1295
1296 let leftovers =
1297 SignedDuration::new(0, dur.subsec_nanos().abs());
1298 wtr.write_fractional_duration(
1299 FractionalUnit::Millisecond,
1300 &leftovers,
1301 )?;
1302 }
1303 Some(FractionalUnit::Microsecond) => {
1304 let mut secs = dur.as_secs();
1305 wtr.write(Unit::Hour, (secs / SECS_PER_HOUR).abs())?;
1306 secs %= MINS_PER_HOUR * SECS_PER_MIN;
1307 wtr.write(Unit::Minute, (secs / SECS_PER_MIN).abs())?;
1308 wtr.write(Unit::Second, (secs % SECS_PER_MIN).abs())?;
1309 let mut nanos = dur.subsec_nanos();
1310 wtr.write(Unit::Millisecond, (nanos / NANOS_PER_MILLI).abs())?;
1311 nanos %= NANOS_PER_MILLI;
1312
1313 let leftovers = SignedDuration::new(0, nanos.abs());
1314 wtr.write_fractional_duration(
1315 FractionalUnit::Microsecond,
1316 &leftovers,
1317 )?;
1318 }
1319 }
1320 wtr.maybe_write_zero()?;
1321 wtr.maybe_write_suffix_sign()?;
1322 Ok(())
1323 }
1324
1325 fn print_duration_hms<W: Write>(
1326 &self,
1327 dur: &SignedDuration,
1328 mut wtr: W,
1329 ) -> Result<(), Error> {
1330 let fmtint =
1336 DecimalFormatter::new().padding(self.padding.unwrap_or(2));
1337 let fmtfraction = FractionalFormatter::new().precision(self.precision);
1338
1339 if dur.is_negative() {
1340 if !matches!(self.direction, Direction::Suffix) {
1341 wtr.write_str("-")?;
1342 }
1343 } else if let Direction::ForceSign = self.direction {
1344 wtr.write_str("+")?;
1345 }
1346 let mut secs = dur.as_secs();
1347 let hours = (secs / (MINS_PER_HOUR * SECS_PER_MIN)).abs();
1349 secs %= MINS_PER_HOUR * SECS_PER_MIN;
1350 let minutes = (secs / SECS_PER_MIN).abs();
1352 secs = (secs % SECS_PER_MIN).abs();
1354
1355 wtr.write_int(&fmtint, hours)?;
1356 wtr.write_str(":")?;
1357 wtr.write_int(&fmtint, minutes)?;
1358 wtr.write_str(":")?;
1359 let fp = FractionalPrinter::from_duration(
1360 &SignedDuration::new(secs, dur.subsec_nanos().abs()),
1362 FractionalUnit::Second,
1363 fmtint,
1364 fmtfraction,
1365 );
1366 fp.print(&mut wtr)?;
1367 if dur.is_negative() {
1368 if matches!(self.direction, Direction::Suffix) {
1369 wtr.write_str(" ago")?;
1370 }
1371 }
1372 Ok(())
1373 }
1374}
1375
1376impl Default for SpanPrinter {
1377 fn default() -> SpanPrinter {
1378 SpanPrinter::new()
1379 }
1380}
1381
1382#[derive(Debug)]
1388struct Designators {
1389 singular: &'static [&'static str],
1390 plural: &'static [&'static str],
1391}
1392
1393impl Designators {
1394 const VERBOSE_SINGULAR: &'static [&'static str] = &[
1395 "nanosecond",
1396 "microsecond",
1397 "millisecond",
1398 "second",
1399 "minute",
1400 "hour",
1401 "day",
1402 "week",
1403 "month",
1404 "year",
1405 ];
1406 const VERBOSE_PLURAL: &'static [&'static str] = &[
1407 "nanoseconds",
1408 "microseconds",
1409 "milliseconds",
1410 "seconds",
1411 "minutes",
1412 "hours",
1413 "days",
1414 "weeks",
1415 "months",
1416 "years",
1417 ];
1418
1419 const SHORT_SINGULAR: &'static [&'static str] =
1420 &["nsec", "µsec", "msec", "sec", "min", "hr", "day", "wk", "mo", "yr"];
1421 const SHORT_PLURAL: &'static [&'static str] = &[
1422 "nsecs", "µsecs", "msecs", "secs", "mins", "hrs", "days", "wks",
1423 "mos", "yrs",
1424 ];
1425
1426 const COMPACT: &'static [&'static str] =
1427 &["ns", "µs", "ms", "s", "m", "h", "d", "w", "mo", "y"];
1428
1429 const HUMAN_TIME_SINGULAR: &'static [&'static str] =
1430 &["ns", "us", "ms", "s", "m", "h", "d", "w", "month", "y"];
1431 const HUMAN_TIME_PLURAL: &'static [&'static str] =
1432 &["ns", "us", "ms", "s", "m", "h", "d", "w", "months", "y"];
1433
1434 fn new(config: Designator) -> Designators {
1435 match config {
1436 Designator::Verbose => Designators {
1437 singular: Designators::VERBOSE_SINGULAR,
1438 plural: Designators::VERBOSE_PLURAL,
1439 },
1440 Designator::Short => Designators {
1441 singular: Designators::SHORT_SINGULAR,
1442 plural: Designators::SHORT_PLURAL,
1443 },
1444 Designator::Compact => Designators {
1445 singular: Designators::COMPACT,
1446 plural: Designators::COMPACT,
1447 },
1448 Designator::HumanTime => Designators {
1449 singular: Designators::HUMAN_TIME_SINGULAR,
1450 plural: Designators::HUMAN_TIME_PLURAL,
1451 },
1452 }
1453 }
1454
1455 fn designator(&self, unit: impl Into<Unit>, plural: bool) -> &'static str {
1456 let unit = unit.into();
1457 let index = unit as usize;
1458 if plural {
1459 self.plural[index]
1460 } else {
1461 self.singular[index]
1462 }
1463 }
1464}
1465
1466#[derive(Debug)]
1472struct DesignatorWriter<'p, 'w, W> {
1473 printer: &'p SpanPrinter,
1474 wtr: &'w mut W,
1475 desig: Designators,
1476 sign: Option<DirectionSign>,
1477 fmtint: DecimalFormatter,
1478 fmtfraction: FractionalFormatter,
1479 written_non_zero_unit: bool,
1480}
1481
1482impl<'p, 'w, W: Write> DesignatorWriter<'p, 'w, W> {
1483 fn new(
1484 printer: &'p SpanPrinter,
1485 wtr: &'w mut W,
1486 has_calendar: bool,
1487 signum: i8,
1488 ) -> DesignatorWriter<'p, 'w, W> {
1489 let desig = Designators::new(printer.designator);
1490 let sign = printer.direction.sign(printer, has_calendar, signum);
1491 let fmtint =
1492 DecimalFormatter::new().padding(printer.padding.unwrap_or(0));
1493 let fmtfraction =
1494 FractionalFormatter::new().precision(printer.precision);
1495 DesignatorWriter {
1496 printer,
1497 wtr,
1498 desig,
1499 sign,
1500 fmtint,
1501 fmtfraction,
1502 written_non_zero_unit: false,
1503 }
1504 }
1505
1506 fn maybe_write_prefix_sign(&mut self) -> Result<(), Error> {
1507 if let Some(DirectionSign::Prefix(sign)) = self.sign {
1508 self.wtr.write_str(sign)?;
1509 }
1510 Ok(())
1511 }
1512
1513 fn maybe_write_suffix_sign(&mut self) -> Result<(), Error> {
1514 if let Some(DirectionSign::Suffix(sign)) = self.sign {
1515 self.wtr.write_str(sign)?;
1516 }
1517 Ok(())
1518 }
1519
1520 fn maybe_write_zero(&mut self) -> Result<(), Error> {
1521 if self.written_non_zero_unit {
1522 return Ok(());
1523 }
1524 let unit = self
1527 .printer
1528 .fractional
1529 .map(Unit::from)
1530 .unwrap_or(self.printer.zero_unit);
1531 self.wtr.write_int(&self.fmtint, 0)?;
1532 self.wtr
1533 .write_str(self.printer.spacing.between_units_and_designators())?;
1534 self.wtr.write_str(self.desig.designator(unit, true))?;
1535 Ok(())
1536 }
1537
1538 fn write(
1539 &mut self,
1540 unit: Unit,
1541 value: impl Into<i64>,
1542 ) -> Result<(), Error> {
1543 let value = value.into();
1544 if value == 0 {
1545 return Ok(());
1546 }
1547 self.finish_preceding()?;
1548 self.written_non_zero_unit = true;
1549 self.wtr.write_int(&self.fmtint, value)?;
1550 self.wtr
1551 .write_str(self.printer.spacing.between_units_and_designators())?;
1552 self.wtr.write_str(self.desig.designator(unit, value != 1))?;
1553 Ok(())
1554 }
1555
1556 fn write_fractional_duration(
1557 &mut self,
1558 unit: FractionalUnit,
1559 duration: &SignedDuration,
1560 ) -> Result<(), Error> {
1561 let fp = FractionalPrinter::from_duration(
1562 duration,
1563 unit,
1564 self.fmtint,
1565 self.fmtfraction,
1566 );
1567 if !fp.must_write_digits() {
1568 return Ok(());
1569 }
1570 self.finish_preceding()?;
1571 self.written_non_zero_unit = true;
1572 fp.print(&mut *self.wtr)?;
1573 self.wtr
1574 .write_str(self.printer.spacing.between_units_and_designators())?;
1575 self.wtr.write_str(self.desig.designator(unit, fp.is_plural()))?;
1576 Ok(())
1577 }
1578
1579 fn finish_preceding(&mut self) -> Result<(), Error> {
1580 if self.written_non_zero_unit {
1581 if self.printer.comma_after_designator {
1582 self.wtr.write_str(",")?;
1583 }
1584 self.wtr.write_str(self.printer.spacing.between_units())?;
1585 }
1586 Ok(())
1587 }
1588}
1589
1590struct FractionalPrinter {
1595 integer: i64,
1596 fraction: i64,
1597 fmtint: DecimalFormatter,
1598 fmtfraction: FractionalFormatter,
1599}
1600
1601impl FractionalPrinter {
1602 fn from_span(
1612 span: &Span,
1613 unit: FractionalUnit,
1614 fmtint: DecimalFormatter,
1615 fmtfraction: FractionalFormatter,
1616 ) -> FractionalPrinter {
1617 debug_assert!(span.largest_unit() <= Unit::from(unit));
1618 let dur = span.to_duration_invariant();
1619 FractionalPrinter::from_duration(&dur, unit, fmtint, fmtfraction)
1620 }
1621
1622 fn from_duration(
1624 dur: &SignedDuration,
1625 unit: FractionalUnit,
1626 fmtint: DecimalFormatter,
1627 fmtfraction: FractionalFormatter,
1628 ) -> FractionalPrinter {
1629 match unit {
1635 FractionalUnit::Hour => {
1636 let integer = (dur.as_secs() / SECS_PER_HOUR).abs();
1637 let fraction = dur.as_nanos() % NANOS_PER_HOUR;
1638 debug_assert!(fraction <= i128::from(i64::MAX));
1640 let mut fraction = i64::try_from(fraction).unwrap();
1641 fraction /= SECS_PER_HOUR;
1643 fraction = fraction.abs();
1645 FractionalPrinter { integer, fraction, fmtint, fmtfraction }
1646 }
1647 FractionalUnit::Minute => {
1648 let integer = (dur.as_secs() / SECS_PER_MIN).abs();
1649 let fraction = dur.as_nanos() % NANOS_PER_MIN;
1650 debug_assert!(fraction <= i128::from(i64::MAX));
1652 let mut fraction = i64::try_from(fraction).unwrap();
1653 fraction /= SECS_PER_MIN;
1655 fraction = fraction.abs();
1657 FractionalPrinter { integer, fraction, fmtint, fmtfraction }
1658 }
1659 FractionalUnit::Second => {
1660 let integer = dur.as_secs();
1661 let fraction = i64::from(dur.subsec_nanos());
1662 FractionalPrinter { integer, fraction, fmtint, fmtfraction }
1663 }
1664 FractionalUnit::Millisecond => {
1665 let integer = i64::try_from(dur.as_millis()).unwrap();
1673 let fraction =
1674 i64::from((dur.subsec_nanos() % NANOS_PER_MILLI) * 1_000);
1675 FractionalPrinter { integer, fraction, fmtint, fmtfraction }
1676 }
1677 FractionalUnit::Microsecond => {
1678 let integer = i64::try_from(dur.as_micros()).unwrap();
1686 let fraction = i64::from(
1687 (dur.subsec_nanos() % NANOS_PER_MICRO) * 1_000_000,
1688 );
1689 FractionalPrinter { integer, fraction, fmtint, fmtfraction }
1690 }
1691 }
1692 }
1693
1694 fn is_zero(&self) -> bool {
1696 self.integer == 0 && self.fraction == 0
1697 }
1698
1699 fn is_plural(&self) -> bool {
1702 self.integer != 1
1703 || (self.fraction != 0
1704 && !self.fmtfraction.has_zero_fixed_precision())
1705 }
1706
1707 fn must_write_digits(&self) -> bool {
1714 !self.is_zero() || self.fmtfraction.has_non_zero_fixed_precision()
1715 }
1716
1717 fn print<W: Write>(&self, mut wtr: W) -> Result<(), Error> {
1723 wtr.write_int(&self.fmtint, self.integer)?;
1724 if self.fmtfraction.will_write_digits(self.fraction) {
1725 wtr.write_str(".")?;
1726 wtr.write_fraction(&self.fmtfraction, self.fraction)?;
1727 }
1728 Ok(())
1729 }
1730}
1731
1732#[cfg(test)]
1733mod tests {
1734 use crate::ToSpan;
1735
1736 use super::*;
1737
1738 #[test]
1739 fn print_span_designator_default() {
1740 let printer = || SpanPrinter::new();
1741 let p = |span| printer().span_to_string(&span);
1742
1743 insta::assert_snapshot!(p(1.second()), @"1s");
1744 insta::assert_snapshot!(p(2.seconds()), @"2s");
1745 insta::assert_snapshot!(p(10.seconds()), @"10s");
1746 insta::assert_snapshot!(p(1.minute().seconds(40)), @"1m 40s");
1747
1748 insta::assert_snapshot!(p(1.minute()), @"1m");
1749 insta::assert_snapshot!(p(2.minutes()), @"2m");
1750 insta::assert_snapshot!(p(10.minutes()), @"10m");
1751 insta::assert_snapshot!(p(1.hour().minutes(40)), @"1h 40m");
1752
1753 insta::assert_snapshot!(p(1.hour()), @"1h");
1754 insta::assert_snapshot!(p(2.hours()), @"2h");
1755 insta::assert_snapshot!(p(10.hours()), @"10h");
1756 insta::assert_snapshot!(p(100.hours()), @"100h");
1757
1758 insta::assert_snapshot!(
1759 p(1.hour().minutes(1).seconds(1)),
1760 @"1h 1m 1s",
1761 );
1762 insta::assert_snapshot!(
1763 p(2.hours().minutes(2).seconds(2)),
1764 @"2h 2m 2s",
1765 );
1766 insta::assert_snapshot!(
1767 p(10.hours().minutes(10).seconds(10)),
1768 @"10h 10m 10s",
1769 );
1770 insta::assert_snapshot!(
1771 p(100.hours().minutes(100).seconds(100)),
1772 @"100h 100m 100s",
1773 );
1774
1775 insta::assert_snapshot!(p(-1.hour()), @"1h ago");
1776 insta::assert_snapshot!(p(-1.hour().seconds(30)), @"1h 30s ago");
1777
1778 insta::assert_snapshot!(
1779 p(1.second().milliseconds(2000)),
1780 @"1s 2000ms",
1781 );
1782 }
1783
1784 #[test]
1785 fn print_span_designator_verbose() {
1786 let printer = || SpanPrinter::new().designator(Designator::Verbose);
1787 let p = |span| printer().span_to_string(&span);
1788
1789 insta::assert_snapshot!(p(1.second()), @"1second");
1790 insta::assert_snapshot!(p(2.seconds()), @"2seconds");
1791 insta::assert_snapshot!(p(10.seconds()), @"10seconds");
1792 insta::assert_snapshot!(p(1.minute().seconds(40)), @"1minute 40seconds");
1793
1794 insta::assert_snapshot!(p(1.minute()), @"1minute");
1795 insta::assert_snapshot!(p(2.minutes()), @"2minutes");
1796 insta::assert_snapshot!(p(10.minutes()), @"10minutes");
1797 insta::assert_snapshot!(p(1.hour().minutes(40)), @"1hour 40minutes");
1798
1799 insta::assert_snapshot!(p(1.hour()), @"1hour");
1800 insta::assert_snapshot!(p(2.hours()), @"2hours");
1801 insta::assert_snapshot!(p(10.hours()), @"10hours");
1802 insta::assert_snapshot!(p(100.hours()), @"100hours");
1803
1804 insta::assert_snapshot!(
1805 p(1.hour().minutes(1).seconds(1)),
1806 @"1hour 1minute 1second",
1807 );
1808 insta::assert_snapshot!(
1809 p(2.hours().minutes(2).seconds(2)),
1810 @"2hours 2minutes 2seconds",
1811 );
1812 insta::assert_snapshot!(
1813 p(10.hours().minutes(10).seconds(10)),
1814 @"10hours 10minutes 10seconds",
1815 );
1816 insta::assert_snapshot!(
1817 p(100.hours().minutes(100).seconds(100)),
1818 @"100hours 100minutes 100seconds",
1819 );
1820
1821 insta::assert_snapshot!(p(-1.hour()), @"1hour ago");
1822 insta::assert_snapshot!(p(-1.hour().seconds(30)), @"1hour 30seconds ago");
1823 }
1824
1825 #[test]
1826 fn print_span_designator_short() {
1827 let printer = || SpanPrinter::new().designator(Designator::Short);
1828 let p = |span| printer().span_to_string(&span);
1829
1830 insta::assert_snapshot!(p(1.second()), @"1sec");
1831 insta::assert_snapshot!(p(2.seconds()), @"2secs");
1832 insta::assert_snapshot!(p(10.seconds()), @"10secs");
1833 insta::assert_snapshot!(p(1.minute().seconds(40)), @"1min 40secs");
1834
1835 insta::assert_snapshot!(p(1.minute()), @"1min");
1836 insta::assert_snapshot!(p(2.minutes()), @"2mins");
1837 insta::assert_snapshot!(p(10.minutes()), @"10mins");
1838 insta::assert_snapshot!(p(1.hour().minutes(40)), @"1hr 40mins");
1839
1840 insta::assert_snapshot!(p(1.hour()), @"1hr");
1841 insta::assert_snapshot!(p(2.hours()), @"2hrs");
1842 insta::assert_snapshot!(p(10.hours()), @"10hrs");
1843 insta::assert_snapshot!(p(100.hours()), @"100hrs");
1844
1845 insta::assert_snapshot!(
1846 p(1.hour().minutes(1).seconds(1)),
1847 @"1hr 1min 1sec",
1848 );
1849 insta::assert_snapshot!(
1850 p(2.hours().minutes(2).seconds(2)),
1851 @"2hrs 2mins 2secs",
1852 );
1853 insta::assert_snapshot!(
1854 p(10.hours().minutes(10).seconds(10)),
1855 @"10hrs 10mins 10secs",
1856 );
1857 insta::assert_snapshot!(
1858 p(100.hours().minutes(100).seconds(100)),
1859 @"100hrs 100mins 100secs",
1860 );
1861
1862 insta::assert_snapshot!(p(-1.hour()), @"1hr ago");
1863 insta::assert_snapshot!(p(-1.hour().seconds(30)), @"1hr 30secs ago");
1864 }
1865
1866 #[test]
1867 fn print_span_designator_compact() {
1868 let printer = || SpanPrinter::new().designator(Designator::Compact);
1869 let p = |span| printer().span_to_string(&span);
1870
1871 insta::assert_snapshot!(p(1.second()), @"1s");
1872 insta::assert_snapshot!(p(2.seconds()), @"2s");
1873 insta::assert_snapshot!(p(10.seconds()), @"10s");
1874 insta::assert_snapshot!(p(1.minute().seconds(40)), @"1m 40s");
1875
1876 insta::assert_snapshot!(p(1.minute()), @"1m");
1877 insta::assert_snapshot!(p(2.minutes()), @"2m");
1878 insta::assert_snapshot!(p(10.minutes()), @"10m");
1879 insta::assert_snapshot!(p(1.hour().minutes(40)), @"1h 40m");
1880
1881 insta::assert_snapshot!(p(1.hour()), @"1h");
1882 insta::assert_snapshot!(p(2.hours()), @"2h");
1883 insta::assert_snapshot!(p(10.hours()), @"10h");
1884 insta::assert_snapshot!(p(100.hours()), @"100h");
1885
1886 insta::assert_snapshot!(
1887 p(1.hour().minutes(1).seconds(1)),
1888 @"1h 1m 1s",
1889 );
1890 insta::assert_snapshot!(
1891 p(2.hours().minutes(2).seconds(2)),
1892 @"2h 2m 2s",
1893 );
1894 insta::assert_snapshot!(
1895 p(10.hours().minutes(10).seconds(10)),
1896 @"10h 10m 10s",
1897 );
1898 insta::assert_snapshot!(
1899 p(100.hours().minutes(100).seconds(100)),
1900 @"100h 100m 100s",
1901 );
1902
1903 insta::assert_snapshot!(p(-1.hour()), @"1h ago");
1904 insta::assert_snapshot!(p(-1.hour().seconds(30)), @"1h 30s ago");
1905 }
1906
1907 #[test]
1908 fn print_span_designator_direction_force() {
1909 let printer = || SpanPrinter::new().direction(Direction::ForceSign);
1910 let p = |span| printer().span_to_string(&span);
1911
1912 insta::assert_snapshot!(p(1.second()), @"+1s");
1913 insta::assert_snapshot!(p(2.seconds()), @"+2s");
1914 insta::assert_snapshot!(p(10.seconds()), @"+10s");
1915 insta::assert_snapshot!(p(1.minute().seconds(40)), @"+1m 40s");
1916
1917 insta::assert_snapshot!(p(1.minute()), @"+1m");
1918 insta::assert_snapshot!(p(2.minutes()), @"+2m");
1919 insta::assert_snapshot!(p(10.minutes()), @"+10m");
1920 insta::assert_snapshot!(p(1.hour().minutes(40)), @"+1h 40m");
1921
1922 insta::assert_snapshot!(p(1.hour()), @"+1h");
1923 insta::assert_snapshot!(p(2.hours()), @"+2h");
1924 insta::assert_snapshot!(p(10.hours()), @"+10h");
1925 insta::assert_snapshot!(p(100.hours()), @"+100h");
1926
1927 insta::assert_snapshot!(
1928 p(1.hour().minutes(1).seconds(1)),
1929 @"+1h 1m 1s",
1930 );
1931 insta::assert_snapshot!(
1932 p(2.hours().minutes(2).seconds(2)),
1933 @"+2h 2m 2s",
1934 );
1935 insta::assert_snapshot!(
1936 p(10.hours().minutes(10).seconds(10)),
1937 @"+10h 10m 10s",
1938 );
1939 insta::assert_snapshot!(
1940 p(100.hours().minutes(100).seconds(100)),
1941 @"+100h 100m 100s",
1942 );
1943
1944 insta::assert_snapshot!(p(-1.hour()), @"-1h");
1945 insta::assert_snapshot!(p(-1.hour().seconds(30)), @"-1h 30s");
1946 }
1947
1948 #[test]
1949 fn print_span_designator_padding() {
1950 let printer = || SpanPrinter::new().padding(2);
1951 let p = |span| printer().span_to_string(&span);
1952
1953 insta::assert_snapshot!(p(1.second()), @"01s");
1954 insta::assert_snapshot!(p(2.seconds()), @"02s");
1955 insta::assert_snapshot!(p(10.seconds()), @"10s");
1956 insta::assert_snapshot!(p(1.minute().seconds(40)), @"01m 40s");
1957
1958 insta::assert_snapshot!(p(1.minute()), @"01m");
1959 insta::assert_snapshot!(p(2.minutes()), @"02m");
1960 insta::assert_snapshot!(p(10.minutes()), @"10m");
1961 insta::assert_snapshot!(p(1.hour().minutes(40)), @"01h 40m");
1962
1963 insta::assert_snapshot!(p(1.hour()), @"01h");
1964 insta::assert_snapshot!(p(2.hours()), @"02h");
1965 insta::assert_snapshot!(p(10.hours()), @"10h");
1966 insta::assert_snapshot!(p(100.hours()), @"100h");
1967
1968 insta::assert_snapshot!(
1969 p(1.hour().minutes(1).seconds(1)),
1970 @"01h 01m 01s",
1971 );
1972 insta::assert_snapshot!(
1973 p(2.hours().minutes(2).seconds(2)),
1974 @"02h 02m 02s",
1975 );
1976 insta::assert_snapshot!(
1977 p(10.hours().minutes(10).seconds(10)),
1978 @"10h 10m 10s",
1979 );
1980 insta::assert_snapshot!(
1981 p(100.hours().minutes(100).seconds(100)),
1982 @"100h 100m 100s",
1983 );
1984
1985 insta::assert_snapshot!(p(-1.hour()), @"01h ago");
1986 insta::assert_snapshot!(p(-1.hour().seconds(30)), @"01h 30s ago");
1987 }
1988
1989 #[test]
1990 fn print_span_designator_spacing_none() {
1991 let printer = || SpanPrinter::new().spacing(Spacing::None);
1992 let p = |span| printer().span_to_string(&span);
1993
1994 insta::assert_snapshot!(p(1.second()), @"1s");
1995 insta::assert_snapshot!(p(2.seconds()), @"2s");
1996 insta::assert_snapshot!(p(10.seconds()), @"10s");
1997 insta::assert_snapshot!(p(1.minute().seconds(40)), @"1m40s");
1998
1999 insta::assert_snapshot!(p(1.minute()), @"1m");
2000 insta::assert_snapshot!(p(2.minutes()), @"2m");
2001 insta::assert_snapshot!(p(10.minutes()), @"10m");
2002 insta::assert_snapshot!(p(1.hour().minutes(40)), @"1h40m");
2003
2004 insta::assert_snapshot!(p(1.hour()), @"1h");
2005 insta::assert_snapshot!(p(2.hours()), @"2h");
2006 insta::assert_snapshot!(p(10.hours()), @"10h");
2007 insta::assert_snapshot!(p(100.hours()), @"100h");
2008
2009 insta::assert_snapshot!(
2010 p(1.hour().minutes(1).seconds(1)),
2011 @"1h1m1s",
2012 );
2013 insta::assert_snapshot!(
2014 p(2.hours().minutes(2).seconds(2)),
2015 @"2h2m2s",
2016 );
2017 insta::assert_snapshot!(
2018 p(10.hours().minutes(10).seconds(10)),
2019 @"10h10m10s",
2020 );
2021 insta::assert_snapshot!(
2022 p(100.hours().minutes(100).seconds(100)),
2023 @"100h100m100s",
2024 );
2025
2026 insta::assert_snapshot!(p(-1.hour()), @"-1h");
2027 insta::assert_snapshot!(p(-1.hour().seconds(30)), @"-1h30s");
2028 }
2029
2030 #[test]
2031 fn print_span_designator_spacing_more() {
2032 let printer =
2033 || SpanPrinter::new().spacing(Spacing::BetweenUnitsAndDesignators);
2034 let p = |span| printer().span_to_string(&span);
2035
2036 insta::assert_snapshot!(p(1.second()), @"1 s");
2037 insta::assert_snapshot!(p(2.seconds()), @"2 s");
2038 insta::assert_snapshot!(p(10.seconds()), @"10 s");
2039 insta::assert_snapshot!(p(1.minute().seconds(40)), @"1 m 40 s");
2040
2041 insta::assert_snapshot!(p(1.minute()), @"1 m");
2042 insta::assert_snapshot!(p(2.minutes()), @"2 m");
2043 insta::assert_snapshot!(p(10.minutes()), @"10 m");
2044 insta::assert_snapshot!(p(1.hour().minutes(40)), @"1 h 40 m");
2045
2046 insta::assert_snapshot!(p(1.hour()), @"1 h");
2047 insta::assert_snapshot!(p(2.hours()), @"2 h");
2048 insta::assert_snapshot!(p(10.hours()), @"10 h");
2049 insta::assert_snapshot!(p(100.hours()), @"100 h");
2050
2051 insta::assert_snapshot!(
2052 p(1.hour().minutes(1).seconds(1)),
2053 @"1 h 1 m 1 s",
2054 );
2055 insta::assert_snapshot!(
2056 p(2.hours().minutes(2).seconds(2)),
2057 @"2 h 2 m 2 s",
2058 );
2059 insta::assert_snapshot!(
2060 p(10.hours().minutes(10).seconds(10)),
2061 @"10 h 10 m 10 s",
2062 );
2063 insta::assert_snapshot!(
2064 p(100.hours().minutes(100).seconds(100)),
2065 @"100 h 100 m 100 s",
2066 );
2067
2068 insta::assert_snapshot!(p(-1.hour()), @"1 h ago");
2069 insta::assert_snapshot!(p(-1.hour().seconds(30)), @"1 h 30 s ago");
2070 }
2071
2072 #[test]
2073 fn print_span_designator_spacing_comma() {
2074 let printer = || {
2075 SpanPrinter::new()
2076 .comma_after_designator(true)
2077 .spacing(Spacing::BetweenUnitsAndDesignators)
2078 };
2079 let p = |span| printer().span_to_string(&span);
2080
2081 insta::assert_snapshot!(p(1.second()), @"1 s");
2082 insta::assert_snapshot!(p(2.seconds()), @"2 s");
2083 insta::assert_snapshot!(p(10.seconds()), @"10 s");
2084 insta::assert_snapshot!(p(1.minute().seconds(40)), @"1 m, 40 s");
2085
2086 insta::assert_snapshot!(p(1.minute()), @"1 m");
2087 insta::assert_snapshot!(p(2.minutes()), @"2 m");
2088 insta::assert_snapshot!(p(10.minutes()), @"10 m");
2089 insta::assert_snapshot!(p(1.hour().minutes(40)), @"1 h, 40 m");
2090
2091 insta::assert_snapshot!(p(1.hour()), @"1 h");
2092 insta::assert_snapshot!(p(2.hours()), @"2 h");
2093 insta::assert_snapshot!(p(10.hours()), @"10 h");
2094 insta::assert_snapshot!(p(100.hours()), @"100 h");
2095
2096 insta::assert_snapshot!(
2097 p(1.hour().minutes(1).seconds(1)),
2098 @"1 h, 1 m, 1 s",
2099 );
2100 insta::assert_snapshot!(
2101 p(2.hours().minutes(2).seconds(2)),
2102 @"2 h, 2 m, 2 s",
2103 );
2104 insta::assert_snapshot!(
2105 p(10.hours().minutes(10).seconds(10)),
2106 @"10 h, 10 m, 10 s",
2107 );
2108 insta::assert_snapshot!(
2109 p(100.hours().minutes(100).seconds(100)),
2110 @"100 h, 100 m, 100 s",
2111 );
2112
2113 insta::assert_snapshot!(p(-1.hour()), @"1 h ago");
2114 insta::assert_snapshot!(p(-1.hour().seconds(30)), @"1 h, 30 s ago");
2115 }
2116
2117 #[test]
2118 fn print_span_designator_fractional_hour() {
2119 let printer =
2120 || SpanPrinter::new().fractional(Some(FractionalUnit::Hour));
2121 let p = |span| printer().span_to_string(&span);
2122 let pp = |precision, span| {
2123 printer().precision(Some(precision)).span_to_string(&span)
2124 };
2125
2126 insta::assert_snapshot!(p(1.hour()), @"1h");
2127 insta::assert_snapshot!(pp(0, 1.hour()), @"1h");
2128 insta::assert_snapshot!(pp(1, 1.hour()), @"1.0h");
2129 insta::assert_snapshot!(pp(2, 1.hour()), @"1.00h");
2130
2131 insta::assert_snapshot!(p(1.hour().minutes(30)), @"1.5h");
2132 insta::assert_snapshot!(pp(0, 1.hour().minutes(30)), @"1h");
2133 insta::assert_snapshot!(pp(1, 1.hour().minutes(30)), @"1.5h");
2134 insta::assert_snapshot!(pp(2, 1.hour().minutes(30)), @"1.50h");
2135
2136 insta::assert_snapshot!(p(1.hour().minutes(3)), @"1.05h");
2137 insta::assert_snapshot!(p(1.hour().minutes(3).nanoseconds(1)), @"1.05h");
2138 insta::assert_snapshot!(p(1.second()), @"0.000277777h");
2139 insta::assert_snapshot!(p(1.second().nanoseconds(1)), @"0.000277777h");
2141 insta::assert_snapshot!(p(0.seconds()), @"0h");
2142 insta::assert_snapshot!(p(1.nanosecond()), @"0h");
2144 }
2145
2146 #[test]
2147 fn print_span_designator_fractional_minute() {
2148 let printer =
2149 || SpanPrinter::new().fractional(Some(FractionalUnit::Minute));
2150 let p = |span| printer().span_to_string(&span);
2151 let pp = |precision, span| {
2152 printer().precision(Some(precision)).span_to_string(&span)
2153 };
2154
2155 insta::assert_snapshot!(p(1.hour()), @"1h");
2156 insta::assert_snapshot!(p(1.hour().minutes(30)), @"1h 30m");
2157
2158 insta::assert_snapshot!(p(1.minute()), @"1m");
2159 insta::assert_snapshot!(pp(0, 1.minute()), @"1m");
2160 insta::assert_snapshot!(pp(1, 1.minute()), @"1.0m");
2161 insta::assert_snapshot!(pp(2, 1.minute()), @"1.00m");
2162
2163 insta::assert_snapshot!(p(1.minute().seconds(30)), @"1.5m");
2164 insta::assert_snapshot!(pp(0, 1.minute().seconds(30)), @"1m");
2165 insta::assert_snapshot!(pp(1, 1.minute().seconds(30)), @"1.5m");
2166 insta::assert_snapshot!(pp(2, 1.minute().seconds(30)), @"1.50m");
2167
2168 insta::assert_snapshot!(p(1.hour().nanoseconds(1)), @"1h");
2169 insta::assert_snapshot!(p(1.minute().seconds(3)), @"1.05m");
2170 insta::assert_snapshot!(p(1.minute().seconds(3).nanoseconds(1)), @"1.05m");
2171 insta::assert_snapshot!(p(1.second()), @"0.016666666m");
2172 insta::assert_snapshot!(p(1.second().nanoseconds(1)), @"0.016666666m");
2174 insta::assert_snapshot!(p(0.seconds()), @"0m");
2175 insta::assert_snapshot!(p(1.nanosecond()), @"0m");
2177 }
2178
2179 #[test]
2180 fn print_span_designator_fractional_second() {
2181 let printer =
2182 || SpanPrinter::new().fractional(Some(FractionalUnit::Second));
2183 let p = |span| printer().span_to_string(&span);
2184 let pp = |precision, span| {
2185 printer().precision(Some(precision)).span_to_string(&span)
2186 };
2187
2188 insta::assert_snapshot!(p(1.hour()), @"1h");
2189 insta::assert_snapshot!(p(1.hour().minutes(30)), @"1h 30m");
2190
2191 insta::assert_snapshot!(p(1.second()), @"1s");
2192 insta::assert_snapshot!(pp(0, 1.second()), @"1s");
2193 insta::assert_snapshot!(pp(1, 1.second()), @"1.0s");
2194 insta::assert_snapshot!(pp(2, 1.second()), @"1.00s");
2195
2196 insta::assert_snapshot!(p(1.second().milliseconds(500)), @"1.5s");
2197 insta::assert_snapshot!(pp(0, 1.second().milliseconds(500)), @"1s");
2198 insta::assert_snapshot!(pp(1, 1.second().milliseconds(500)), @"1.5s");
2199 insta::assert_snapshot!(pp(2, 1.second().milliseconds(500)), @"1.50s");
2200
2201 insta::assert_snapshot!(p(1.second().nanoseconds(1)), @"1.000000001s");
2202 insta::assert_snapshot!(p(1.nanosecond()), @"0.000000001s");
2203 insta::assert_snapshot!(p(0.seconds()), @"0s");
2204
2205 insta::assert_snapshot!(p(1.second().milliseconds(2000)), @"3s");
2206 }
2207
2208 #[test]
2209 fn print_span_designator_fractional_millisecond() {
2210 let printer = || {
2211 SpanPrinter::new().fractional(Some(FractionalUnit::Millisecond))
2212 };
2213 let p = |span| printer().span_to_string(&span);
2214 let pp = |precision, span| {
2215 printer().precision(Some(precision)).span_to_string(&span)
2216 };
2217
2218 insta::assert_snapshot!(p(1.hour()), @"1h");
2219 insta::assert_snapshot!(p(1.hour().minutes(30)), @"1h 30m");
2220 insta::assert_snapshot!(
2221 p(1.hour().minutes(30).seconds(10)),
2222 @"1h 30m 10s",
2223 );
2224
2225 insta::assert_snapshot!(p(1.second()), @"1s");
2226 insta::assert_snapshot!(pp(0, 1.second()), @"1s");
2227 insta::assert_snapshot!(pp(1, 1.second()), @"1s 0.0ms");
2228 insta::assert_snapshot!(pp(2, 1.second()), @"1s 0.00ms");
2229
2230 insta::assert_snapshot!(p(1.second().milliseconds(500)), @"1s 500ms");
2231 insta::assert_snapshot!(
2232 pp(0, 1.second().milliseconds(1).microseconds(500)),
2233 @"1s 1ms",
2234 );
2235 insta::assert_snapshot!(
2236 pp(1, 1.second().milliseconds(1).microseconds(500)),
2237 @"1s 1.5ms",
2238 );
2239 insta::assert_snapshot!(
2240 pp(2, 1.second().milliseconds(1).microseconds(500)),
2241 @"1s 1.50ms",
2242 );
2243
2244 insta::assert_snapshot!(p(1.millisecond().nanoseconds(1)), @"1.000001ms");
2245 insta::assert_snapshot!(p(1.nanosecond()), @"0.000001ms");
2246 insta::assert_snapshot!(p(0.seconds()), @"0ms");
2247 }
2248
2249 #[test]
2250 fn print_span_designator_fractional_microsecond() {
2251 let printer = || {
2252 SpanPrinter::new().fractional(Some(FractionalUnit::Microsecond))
2253 };
2254 let p = |span| printer().span_to_string(&span);
2255 let pp = |precision, span| {
2256 printer().precision(Some(precision)).span_to_string(&span)
2257 };
2258
2259 insta::assert_snapshot!(p(1.hour()), @"1h");
2260 insta::assert_snapshot!(p(1.hour().minutes(30)), @"1h 30m");
2261 insta::assert_snapshot!(
2262 p(1.hour().minutes(30).seconds(10)),
2263 @"1h 30m 10s",
2264 );
2265
2266 insta::assert_snapshot!(p(1.second()), @"1s");
2267 insta::assert_snapshot!(pp(0, 1.second()), @"1s");
2268 insta::assert_snapshot!(pp(1, 1.second()), @"1s 0.0µs");
2269 insta::assert_snapshot!(pp(2, 1.second()), @"1s 0.00µs");
2270
2271 insta::assert_snapshot!(p(1.second().milliseconds(500)), @"1s 500ms");
2272 insta::assert_snapshot!(
2273 pp(0, 1.second().milliseconds(1).microseconds(500)),
2274 @"1s 1ms 500µs",
2275 );
2276 insta::assert_snapshot!(
2277 pp(1, 1.second().milliseconds(1).microseconds(500)),
2278 @"1s 1ms 500.0µs",
2279 );
2280 insta::assert_snapshot!(
2281 pp(2, 1.second().milliseconds(1).microseconds(500)),
2282 @"1s 1ms 500.00µs",
2283 );
2284
2285 insta::assert_snapshot!(
2286 p(1.millisecond().nanoseconds(1)),
2287 @"1ms 0.001µs",
2288 );
2289 insta::assert_snapshot!(p(1.nanosecond()), @"0.001µs");
2290 insta::assert_snapshot!(p(0.second()), @"0µs");
2291 }
2292
2293 #[test]
2294 fn print_duration_designator_default() {
2295 let printer = || SpanPrinter::new();
2296 let p = |secs| {
2297 printer().duration_to_string(&SignedDuration::from_secs(secs))
2298 };
2299
2300 insta::assert_snapshot!(p(1), @"1s");
2301 insta::assert_snapshot!(p(2), @"2s");
2302 insta::assert_snapshot!(p(10), @"10s");
2303 insta::assert_snapshot!(p(100), @"1m 40s");
2304
2305 insta::assert_snapshot!(p(1 * 60), @"1m");
2306 insta::assert_snapshot!(p(2 * 60), @"2m");
2307 insta::assert_snapshot!(p(10 * 60), @"10m");
2308 insta::assert_snapshot!(p(100 * 60), @"1h 40m");
2309
2310 insta::assert_snapshot!(p(1 * 60 * 60), @"1h");
2311 insta::assert_snapshot!(p(2 * 60 * 60), @"2h");
2312 insta::assert_snapshot!(p(10 * 60 * 60), @"10h");
2313 insta::assert_snapshot!(p(100 * 60 * 60), @"100h");
2314
2315 insta::assert_snapshot!(
2316 p(60 * 60 + 60 + 1),
2317 @"1h 1m 1s",
2318 );
2319 insta::assert_snapshot!(
2320 p(2 * 60 * 60 + 2 * 60 + 2),
2321 @"2h 2m 2s",
2322 );
2323 insta::assert_snapshot!(
2324 p(10 * 60 * 60 + 10 * 60 + 10),
2325 @"10h 10m 10s",
2326 );
2327 insta::assert_snapshot!(
2328 p(100 * 60 * 60 + 100 * 60 + 100),
2329 @"101h 41m 40s",
2330 );
2331
2332 insta::assert_snapshot!(p(-1 * 60 * 60), @"1h ago");
2333 insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"1h 30s ago");
2334 }
2335
2336 #[test]
2337 fn print_duration_designator_verbose() {
2338 let printer = || SpanPrinter::new().designator(Designator::Verbose);
2339 let p = |secs| {
2340 printer().duration_to_string(&SignedDuration::from_secs(secs))
2341 };
2342
2343 insta::assert_snapshot!(p(1), @"1second");
2344 insta::assert_snapshot!(p(2), @"2seconds");
2345 insta::assert_snapshot!(p(10), @"10seconds");
2346 insta::assert_snapshot!(p(100), @"1minute 40seconds");
2347
2348 insta::assert_snapshot!(p(1 * 60), @"1minute");
2349 insta::assert_snapshot!(p(2 * 60), @"2minutes");
2350 insta::assert_snapshot!(p(10 * 60), @"10minutes");
2351 insta::assert_snapshot!(p(100 * 60), @"1hour 40minutes");
2352
2353 insta::assert_snapshot!(p(1 * 60 * 60), @"1hour");
2354 insta::assert_snapshot!(p(2 * 60 * 60), @"2hours");
2355 insta::assert_snapshot!(p(10 * 60 * 60), @"10hours");
2356 insta::assert_snapshot!(p(100 * 60 * 60), @"100hours");
2357
2358 insta::assert_snapshot!(
2359 p(60 * 60 + 60 + 1),
2360 @"1hour 1minute 1second",
2361 );
2362 insta::assert_snapshot!(
2363 p(2 * 60 * 60 + 2 * 60 + 2),
2364 @"2hours 2minutes 2seconds",
2365 );
2366 insta::assert_snapshot!(
2367 p(10 * 60 * 60 + 10 * 60 + 10),
2368 @"10hours 10minutes 10seconds",
2369 );
2370 insta::assert_snapshot!(
2371 p(100 * 60 * 60 + 100 * 60 + 100),
2372 @"101hours 41minutes 40seconds",
2373 );
2374
2375 insta::assert_snapshot!(p(-1 * 60 * 60), @"1hour ago");
2376 insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"1hour 30seconds ago");
2377 }
2378
2379 #[test]
2380 fn print_duration_designator_short() {
2381 let printer = || SpanPrinter::new().designator(Designator::Short);
2382 let p = |secs| {
2383 printer().duration_to_string(&SignedDuration::from_secs(secs))
2384 };
2385
2386 insta::assert_snapshot!(p(1), @"1sec");
2387 insta::assert_snapshot!(p(2), @"2secs");
2388 insta::assert_snapshot!(p(10), @"10secs");
2389 insta::assert_snapshot!(p(100), @"1min 40secs");
2390
2391 insta::assert_snapshot!(p(1 * 60), @"1min");
2392 insta::assert_snapshot!(p(2 * 60), @"2mins");
2393 insta::assert_snapshot!(p(10 * 60), @"10mins");
2394 insta::assert_snapshot!(p(100 * 60), @"1hr 40mins");
2395
2396 insta::assert_snapshot!(p(1 * 60 * 60), @"1hr");
2397 insta::assert_snapshot!(p(2 * 60 * 60), @"2hrs");
2398 insta::assert_snapshot!(p(10 * 60 * 60), @"10hrs");
2399 insta::assert_snapshot!(p(100 * 60 * 60), @"100hrs");
2400
2401 insta::assert_snapshot!(
2402 p(60 * 60 + 60 + 1),
2403 @"1hr 1min 1sec",
2404 );
2405 insta::assert_snapshot!(
2406 p(2 * 60 * 60 + 2 * 60 + 2),
2407 @"2hrs 2mins 2secs",
2408 );
2409 insta::assert_snapshot!(
2410 p(10 * 60 * 60 + 10 * 60 + 10),
2411 @"10hrs 10mins 10secs",
2412 );
2413 insta::assert_snapshot!(
2414 p(100 * 60 * 60 + 100 * 60 + 100),
2415 @"101hrs 41mins 40secs",
2416 );
2417
2418 insta::assert_snapshot!(p(-1 * 60 * 60), @"1hr ago");
2419 insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"1hr 30secs ago");
2420 }
2421
2422 #[test]
2423 fn print_duration_designator_compact() {
2424 let printer = || SpanPrinter::new().designator(Designator::Compact);
2425 let p = |secs| {
2426 printer().duration_to_string(&SignedDuration::from_secs(secs))
2427 };
2428
2429 insta::assert_snapshot!(p(1), @"1s");
2430 insta::assert_snapshot!(p(2), @"2s");
2431 insta::assert_snapshot!(p(10), @"10s");
2432 insta::assert_snapshot!(p(100), @"1m 40s");
2433
2434 insta::assert_snapshot!(p(1 * 60), @"1m");
2435 insta::assert_snapshot!(p(2 * 60), @"2m");
2436 insta::assert_snapshot!(p(10 * 60), @"10m");
2437 insta::assert_snapshot!(p(100 * 60), @"1h 40m");
2438
2439 insta::assert_snapshot!(p(1 * 60 * 60), @"1h");
2440 insta::assert_snapshot!(p(2 * 60 * 60), @"2h");
2441 insta::assert_snapshot!(p(10 * 60 * 60), @"10h");
2442 insta::assert_snapshot!(p(100 * 60 * 60), @"100h");
2443
2444 insta::assert_snapshot!(
2445 p(60 * 60 + 60 + 1),
2446 @"1h 1m 1s",
2447 );
2448 insta::assert_snapshot!(
2449 p(2 * 60 * 60 + 2 * 60 + 2),
2450 @"2h 2m 2s",
2451 );
2452 insta::assert_snapshot!(
2453 p(10 * 60 * 60 + 10 * 60 + 10),
2454 @"10h 10m 10s",
2455 );
2456 insta::assert_snapshot!(
2457 p(100 * 60 * 60 + 100 * 60 + 100),
2458 @"101h 41m 40s",
2459 );
2460
2461 insta::assert_snapshot!(p(-1 * 60 * 60), @"1h ago");
2462 insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"1h 30s ago");
2463 }
2464
2465 #[test]
2466 fn print_duration_designator_direction_force() {
2467 let printer = || SpanPrinter::new().direction(Direction::ForceSign);
2468 let p = |secs| {
2469 printer().duration_to_string(&SignedDuration::from_secs(secs))
2470 };
2471
2472 insta::assert_snapshot!(p(1), @"+1s");
2473 insta::assert_snapshot!(p(2), @"+2s");
2474 insta::assert_snapshot!(p(10), @"+10s");
2475 insta::assert_snapshot!(p(100), @"+1m 40s");
2476
2477 insta::assert_snapshot!(p(1 * 60), @"+1m");
2478 insta::assert_snapshot!(p(2 * 60), @"+2m");
2479 insta::assert_snapshot!(p(10 * 60), @"+10m");
2480 insta::assert_snapshot!(p(100 * 60), @"+1h 40m");
2481
2482 insta::assert_snapshot!(p(1 * 60 * 60), @"+1h");
2483 insta::assert_snapshot!(p(2 * 60 * 60), @"+2h");
2484 insta::assert_snapshot!(p(10 * 60 * 60), @"+10h");
2485 insta::assert_snapshot!(p(100 * 60 * 60), @"+100h");
2486
2487 insta::assert_snapshot!(
2488 p(60 * 60 + 60 + 1),
2489 @"+1h 1m 1s",
2490 );
2491 insta::assert_snapshot!(
2492 p(2 * 60 * 60 + 2 * 60 + 2),
2493 @"+2h 2m 2s",
2494 );
2495 insta::assert_snapshot!(
2496 p(10 * 60 * 60 + 10 * 60 + 10),
2497 @"+10h 10m 10s",
2498 );
2499 insta::assert_snapshot!(
2500 p(100 * 60 * 60 + 100 * 60 + 100),
2501 @"+101h 41m 40s",
2502 );
2503
2504 insta::assert_snapshot!(p(-1 * 60 * 60), @"-1h");
2505 insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"-1h 30s");
2506 }
2507
2508 #[test]
2509 fn print_duration_designator_padding() {
2510 let printer = || SpanPrinter::new().padding(2);
2511 let p = |secs| {
2512 printer().duration_to_string(&SignedDuration::from_secs(secs))
2513 };
2514
2515 insta::assert_snapshot!(p(1), @"01s");
2516 insta::assert_snapshot!(p(2), @"02s");
2517 insta::assert_snapshot!(p(10), @"10s");
2518 insta::assert_snapshot!(p(100), @"01m 40s");
2519
2520 insta::assert_snapshot!(p(1 * 60), @"01m");
2521 insta::assert_snapshot!(p(2 * 60), @"02m");
2522 insta::assert_snapshot!(p(10 * 60), @"10m");
2523 insta::assert_snapshot!(p(100 * 60), @"01h 40m");
2524
2525 insta::assert_snapshot!(p(1 * 60 * 60), @"01h");
2526 insta::assert_snapshot!(p(2 * 60 * 60), @"02h");
2527 insta::assert_snapshot!(p(10 * 60 * 60), @"10h");
2528 insta::assert_snapshot!(p(100 * 60 * 60), @"100h");
2529
2530 insta::assert_snapshot!(
2531 p(60 * 60 + 60 + 1),
2532 @"01h 01m 01s",
2533 );
2534 insta::assert_snapshot!(
2535 p(2 * 60 * 60 + 2 * 60 + 2),
2536 @"02h 02m 02s",
2537 );
2538 insta::assert_snapshot!(
2539 p(10 * 60 * 60 + 10 * 60 + 10),
2540 @"10h 10m 10s",
2541 );
2542 insta::assert_snapshot!(
2543 p(100 * 60 * 60 + 100 * 60 + 100),
2544 @"101h 41m 40s",
2545 );
2546
2547 insta::assert_snapshot!(p(-1 * 60 * 60), @"01h ago");
2548 insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"01h 30s ago");
2549 }
2550
2551 #[test]
2552 fn print_duration_designator_spacing_none() {
2553 let printer = || SpanPrinter::new().spacing(Spacing::None);
2554 let p = |secs| {
2555 printer().duration_to_string(&SignedDuration::from_secs(secs))
2556 };
2557
2558 insta::assert_snapshot!(p(1), @"1s");
2559 insta::assert_snapshot!(p(2), @"2s");
2560 insta::assert_snapshot!(p(10), @"10s");
2561 insta::assert_snapshot!(p(100), @"1m40s");
2562
2563 insta::assert_snapshot!(p(1 * 60), @"1m");
2564 insta::assert_snapshot!(p(2 * 60), @"2m");
2565 insta::assert_snapshot!(p(10 * 60), @"10m");
2566 insta::assert_snapshot!(p(100 * 60), @"1h40m");
2567
2568 insta::assert_snapshot!(p(1 * 60 * 60), @"1h");
2569 insta::assert_snapshot!(p(2 * 60 * 60), @"2h");
2570 insta::assert_snapshot!(p(10 * 60 * 60), @"10h");
2571 insta::assert_snapshot!(p(100 * 60 * 60), @"100h");
2572
2573 insta::assert_snapshot!(
2574 p(60 * 60 + 60 + 1),
2575 @"1h1m1s",
2576 );
2577 insta::assert_snapshot!(
2578 p(2 * 60 * 60 + 2 * 60 + 2),
2579 @"2h2m2s",
2580 );
2581 insta::assert_snapshot!(
2582 p(10 * 60 * 60 + 10 * 60 + 10),
2583 @"10h10m10s",
2584 );
2585 insta::assert_snapshot!(
2586 p(100 * 60 * 60 + 100 * 60 + 100),
2587 @"101h41m40s",
2588 );
2589
2590 insta::assert_snapshot!(p(-1 * 60 * 60), @"-1h");
2591 insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"-1h30s");
2592 }
2593
2594 #[test]
2595 fn print_duration_designator_spacing_more() {
2596 let printer =
2597 || SpanPrinter::new().spacing(Spacing::BetweenUnitsAndDesignators);
2598 let p = |secs| {
2599 printer().duration_to_string(&SignedDuration::from_secs(secs))
2600 };
2601
2602 insta::assert_snapshot!(p(1), @"1 s");
2603 insta::assert_snapshot!(p(2), @"2 s");
2604 insta::assert_snapshot!(p(10), @"10 s");
2605 insta::assert_snapshot!(p(100), @"1 m 40 s");
2606
2607 insta::assert_snapshot!(p(1 * 60), @"1 m");
2608 insta::assert_snapshot!(p(2 * 60), @"2 m");
2609 insta::assert_snapshot!(p(10 * 60), @"10 m");
2610 insta::assert_snapshot!(p(100 * 60), @"1 h 40 m");
2611
2612 insta::assert_snapshot!(p(1 * 60 * 60), @"1 h");
2613 insta::assert_snapshot!(p(2 * 60 * 60), @"2 h");
2614 insta::assert_snapshot!(p(10 * 60 * 60), @"10 h");
2615 insta::assert_snapshot!(p(100 * 60 * 60), @"100 h");
2616
2617 insta::assert_snapshot!(
2618 p(60 * 60 + 60 + 1),
2619 @"1 h 1 m 1 s",
2620 );
2621 insta::assert_snapshot!(
2622 p(2 * 60 * 60 + 2 * 60 + 2),
2623 @"2 h 2 m 2 s",
2624 );
2625 insta::assert_snapshot!(
2626 p(10 * 60 * 60 + 10 * 60 + 10),
2627 @"10 h 10 m 10 s",
2628 );
2629 insta::assert_snapshot!(
2630 p(100 * 60 * 60 + 100 * 60 + 100),
2631 @"101 h 41 m 40 s",
2632 );
2633
2634 insta::assert_snapshot!(p(-1 * 60 * 60), @"1 h ago");
2635 insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"1 h 30 s ago");
2636 }
2637
2638 #[test]
2639 fn print_duration_designator_spacing_comma() {
2640 let printer = || {
2641 SpanPrinter::new()
2642 .comma_after_designator(true)
2643 .spacing(Spacing::BetweenUnitsAndDesignators)
2644 };
2645 let p = |secs| {
2646 printer().duration_to_string(&SignedDuration::from_secs(secs))
2647 };
2648
2649 insta::assert_snapshot!(p(1), @"1 s");
2650 insta::assert_snapshot!(p(2), @"2 s");
2651 insta::assert_snapshot!(p(10), @"10 s");
2652 insta::assert_snapshot!(p(100), @"1 m, 40 s");
2653
2654 insta::assert_snapshot!(p(1 * 60), @"1 m");
2655 insta::assert_snapshot!(p(2 * 60), @"2 m");
2656 insta::assert_snapshot!(p(10 * 60), @"10 m");
2657 insta::assert_snapshot!(p(100 * 60), @"1 h, 40 m");
2658
2659 insta::assert_snapshot!(p(1 * 60 * 60), @"1 h");
2660 insta::assert_snapshot!(p(2 * 60 * 60), @"2 h");
2661 insta::assert_snapshot!(p(10 * 60 * 60), @"10 h");
2662 insta::assert_snapshot!(p(100 * 60 * 60), @"100 h");
2663
2664 insta::assert_snapshot!(
2665 p(60 * 60 + 60 + 1),
2666 @"1 h, 1 m, 1 s",
2667 );
2668 insta::assert_snapshot!(
2669 p(2 * 60 * 60 + 2 * 60 + 2),
2670 @"2 h, 2 m, 2 s",
2671 );
2672 insta::assert_snapshot!(
2673 p(10 * 60 * 60 + 10 * 60 + 10),
2674 @"10 h, 10 m, 10 s",
2675 );
2676 insta::assert_snapshot!(
2677 p(100 * 60 * 60 + 100 * 60 + 100),
2678 @"101 h, 41 m, 40 s",
2679 );
2680
2681 insta::assert_snapshot!(p(-1 * 60 * 60), @"1 h ago");
2682 insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"1 h, 30 s ago");
2683 }
2684
2685 #[test]
2686 fn print_duration_designator_fractional_hour() {
2687 let printer =
2688 || SpanPrinter::new().fractional(Some(FractionalUnit::Hour));
2689 let p = |secs, nanos| {
2690 printer().duration_to_string(&SignedDuration::new(secs, nanos))
2691 };
2692 let pp = |precision, secs, nanos| {
2693 printer()
2694 .precision(Some(precision))
2695 .duration_to_string(&SignedDuration::new(secs, nanos))
2696 };
2697
2698 insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
2699 insta::assert_snapshot!(pp(0, 1 * 60 * 60, 0), @"1h");
2700 insta::assert_snapshot!(pp(1, 1 * 60 * 60, 0), @"1.0h");
2701 insta::assert_snapshot!(pp(2, 1 * 60 * 60, 0), @"1.00h");
2702
2703 insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1.5h");
2704 insta::assert_snapshot!(pp(0, 1 * 60 * 60 + 30 * 60, 0), @"1h");
2705 insta::assert_snapshot!(pp(1, 1 * 60 * 60 + 30 * 60, 0), @"1.5h");
2706 insta::assert_snapshot!(pp(2, 1 * 60 * 60 + 30 * 60, 0), @"1.50h");
2707
2708 insta::assert_snapshot!(p(1 * 60 * 60 + 3 * 60, 0), @"1.05h");
2709 insta::assert_snapshot!(p(1 * 60 * 60 + 3 * 60, 1), @"1.05h");
2710 insta::assert_snapshot!(p(1, 0), @"0.000277777h");
2711 insta::assert_snapshot!(p(1, 1), @"0.000277777h");
2713 insta::assert_snapshot!(p(0, 0), @"0h");
2714 insta::assert_snapshot!(p(0, 1), @"0h");
2716
2717 insta::assert_snapshot!(
2718 printer().duration_to_string(&SignedDuration::MIN),
2719 @"2562047788015215.502499999h ago",
2720 );
2721 }
2722
2723 #[test]
2724 fn print_duration_designator_fractional_minute() {
2725 let printer =
2726 || SpanPrinter::new().fractional(Some(FractionalUnit::Minute));
2727 let p = |secs, nanos| {
2728 printer().duration_to_string(&SignedDuration::new(secs, nanos))
2729 };
2730 let pp = |precision, secs, nanos| {
2731 printer()
2732 .precision(Some(precision))
2733 .duration_to_string(&SignedDuration::new(secs, nanos))
2734 };
2735
2736 insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
2737 insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1h 30m");
2738
2739 insta::assert_snapshot!(p(60, 0), @"1m");
2740 insta::assert_snapshot!(pp(0, 60, 0), @"1m");
2741 insta::assert_snapshot!(pp(1, 60, 0), @"1.0m");
2742 insta::assert_snapshot!(pp(2, 60, 0), @"1.00m");
2743
2744 insta::assert_snapshot!(p(90, 0), @"1.5m");
2745 insta::assert_snapshot!(pp(0, 90, 0), @"1m");
2746 insta::assert_snapshot!(pp(1, 90, 0), @"1.5m");
2747 insta::assert_snapshot!(pp(2, 90, 0), @"1.50m");
2748
2749 insta::assert_snapshot!(p(1 * 60 * 60, 1), @"1h");
2750 insta::assert_snapshot!(p(63, 0), @"1.05m");
2751 insta::assert_snapshot!(p(63, 1), @"1.05m");
2752 insta::assert_snapshot!(p(1, 0), @"0.016666666m");
2753 insta::assert_snapshot!(p(1, 1), @"0.016666666m");
2755 insta::assert_snapshot!(p(0, 0), @"0m");
2756 insta::assert_snapshot!(p(0, 1), @"0m");
2758
2759 insta::assert_snapshot!(
2760 printer().duration_to_string(&SignedDuration::MIN),
2761 @"2562047788015215h 30.149999999m ago",
2762 );
2763 }
2764
2765 #[test]
2766 fn print_duration_designator_fractional_second() {
2767 let printer =
2768 || SpanPrinter::new().fractional(Some(FractionalUnit::Second));
2769 let p = |secs, nanos| {
2770 printer().duration_to_string(&SignedDuration::new(secs, nanos))
2771 };
2772 let pp = |precision, secs, nanos| {
2773 printer()
2774 .precision(Some(precision))
2775 .duration_to_string(&SignedDuration::new(secs, nanos))
2776 };
2777
2778 insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
2779 insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1h 30m");
2780
2781 insta::assert_snapshot!(p(1, 0), @"1s");
2782 insta::assert_snapshot!(pp(0, 1, 0), @"1s");
2783 insta::assert_snapshot!(pp(1, 1, 0), @"1.0s");
2784 insta::assert_snapshot!(pp(2, 1, 0), @"1.00s");
2785
2786 insta::assert_snapshot!(p(1, 500_000_000), @"1.5s");
2787 insta::assert_snapshot!(pp(0, 1, 500_000_000), @"1s");
2788 insta::assert_snapshot!(pp(1, 1, 500_000_000), @"1.5s");
2789 insta::assert_snapshot!(pp(2, 1, 500_000_000), @"1.50s");
2790
2791 insta::assert_snapshot!(p(1, 1), @"1.000000001s");
2792 insta::assert_snapshot!(p(0, 1), @"0.000000001s");
2793 insta::assert_snapshot!(p(0, 0), @"0s");
2794
2795 insta::assert_snapshot!(
2796 printer().duration_to_string(&SignedDuration::MIN),
2797 @"2562047788015215h 30m 8.999999999s ago",
2798 );
2799 }
2800
2801 #[test]
2802 fn print_duration_designator_fractional_millisecond() {
2803 let printer = || {
2804 SpanPrinter::new().fractional(Some(FractionalUnit::Millisecond))
2805 };
2806 let p = |secs, nanos| {
2807 printer().duration_to_string(&SignedDuration::new(secs, nanos))
2808 };
2809 let pp = |precision, secs, nanos| {
2810 printer()
2811 .precision(Some(precision))
2812 .duration_to_string(&SignedDuration::new(secs, nanos))
2813 };
2814
2815 insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
2816 insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1h 30m");
2817 insta::assert_snapshot!(
2818 p(1 * 60 * 60 + 30 * 60 + 10, 0),
2819 @"1h 30m 10s",
2820 );
2821
2822 insta::assert_snapshot!(p(1, 0), @"1s");
2823 insta::assert_snapshot!(pp(0, 1, 0), @"1s");
2824 insta::assert_snapshot!(pp(1, 1, 0), @"1s 0.0ms");
2825 insta::assert_snapshot!(pp(2, 1, 0), @"1s 0.00ms");
2826
2827 insta::assert_snapshot!(p(1, 500_000_000), @"1s 500ms");
2828 insta::assert_snapshot!(pp(0, 1, 1_500_000), @"1s 1ms");
2829 insta::assert_snapshot!(pp(1, 1, 1_500_000), @"1s 1.5ms");
2830 insta::assert_snapshot!(pp(2, 1, 1_500_000), @"1s 1.50ms");
2831
2832 insta::assert_snapshot!(p(0, 1_000_001), @"1.000001ms");
2833 insta::assert_snapshot!(p(0, 0_000_001), @"0.000001ms");
2834 insta::assert_snapshot!(p(0, 0), @"0ms");
2835
2836 insta::assert_snapshot!(
2837 printer().duration_to_string(&SignedDuration::MIN),
2838 @"2562047788015215h 30m 8s 999.999999ms ago",
2839 );
2840 }
2841
2842 #[test]
2843 fn print_duration_designator_fractional_microsecond() {
2844 let printer = || {
2845 SpanPrinter::new().fractional(Some(FractionalUnit::Microsecond))
2846 };
2847 let p = |secs, nanos| {
2848 printer().duration_to_string(&SignedDuration::new(secs, nanos))
2849 };
2850 let pp = |precision, secs, nanos| {
2851 printer()
2852 .precision(Some(precision))
2853 .duration_to_string(&SignedDuration::new(secs, nanos))
2854 };
2855
2856 insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
2857 insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1h 30m");
2858 insta::assert_snapshot!(
2859 p(1 * 60 * 60 + 30 * 60 + 10, 0),
2860 @"1h 30m 10s",
2861 );
2862
2863 insta::assert_snapshot!(p(1, 0), @"1s");
2864 insta::assert_snapshot!(pp(0, 1, 0), @"1s");
2865 insta::assert_snapshot!(pp(1, 1, 0), @"1s 0.0µs");
2866 insta::assert_snapshot!(pp(2, 1, 0), @"1s 0.00µs");
2867
2868 insta::assert_snapshot!(p(1, 500_000_000), @"1s 500ms");
2869 insta::assert_snapshot!(pp(0, 1, 1_500_000), @"1s 1ms 500µs");
2870 insta::assert_snapshot!(pp(1, 1, 1_500_000), @"1s 1ms 500.0µs");
2871 insta::assert_snapshot!(pp(2, 1, 1_500_000), @"1s 1ms 500.00µs");
2872
2873 insta::assert_snapshot!(p(0, 1_000_001), @"1ms 0.001µs");
2874 insta::assert_snapshot!(p(0, 0_000_001), @"0.001µs");
2875 insta::assert_snapshot!(p(0, 0), @"0µs");
2876
2877 insta::assert_snapshot!(
2878 printer().duration_to_string(&SignedDuration::MIN),
2879 @"2562047788015215h 30m 8s 999ms 999.999µs ago",
2880 );
2881 }
2882
2883 #[test]
2884 fn print_span_hms() {
2885 let printer = || SpanPrinter::new().hours_minutes_seconds(true);
2886 let p = |span| printer().span_to_string(&span);
2887
2888 insta::assert_snapshot!(p(1.second()), @"00:00:01");
2889 insta::assert_snapshot!(p(2.seconds()), @"00:00:02");
2890 insta::assert_snapshot!(p(10.seconds()), @"00:00:10");
2891 insta::assert_snapshot!(p(100.seconds()), @"00:00:100");
2892
2893 insta::assert_snapshot!(p(1.minute()), @"00:01:00");
2894 insta::assert_snapshot!(p(2.minutes()), @"00:02:00");
2895 insta::assert_snapshot!(p(10.minutes()), @"00:10:00");
2896 insta::assert_snapshot!(p(100.minutes()), @"00:100:00");
2897
2898 insta::assert_snapshot!(p(1.hour()), @"01:00:00");
2899 insta::assert_snapshot!(p(2.hours()), @"02:00:00");
2900 insta::assert_snapshot!(p(10.hours()), @"10:00:00");
2901 insta::assert_snapshot!(p(100.hours()), @"100:00:00");
2902
2903 insta::assert_snapshot!(
2904 p(1.hour().minutes(1).seconds(1)),
2905 @"01:01:01",
2906 );
2907 insta::assert_snapshot!(
2908 p(2.hours().minutes(2).seconds(2)),
2909 @"02:02:02",
2910 );
2911 insta::assert_snapshot!(
2912 p(10.hours().minutes(10).seconds(10)),
2913 @"10:10:10",
2914 );
2915 insta::assert_snapshot!(
2916 p(100.hours().minutes(100).seconds(100)),
2917 @"100:100:100",
2918 );
2919
2920 insta::assert_snapshot!(
2921 p(1.day().hours(1).minutes(1).seconds(1)),
2922 @"1d 01:01:01",
2923 );
2924 insta::assert_snapshot!(
2925 p(1.day()),
2926 @"1d 00:00:00",
2927 );
2928 insta::assert_snapshot!(
2929 p(1.day().seconds(2)),
2930 @"1d 00:00:02",
2931 );
2932 }
2933
2934 #[test]
2935 fn print_span_hms_fmt() {
2936 let printer = || {
2937 SpanPrinter::new()
2938 .hours_minutes_seconds(true)
2939 .comma_after_designator(true)
2940 .spacing(Spacing::BetweenUnitsAndDesignators)
2941 };
2942 let p = |span| printer().span_to_string(&span);
2943
2944 insta::assert_snapshot!(
2945 p(1.day().hours(1).minutes(1).seconds(1)),
2946 @"1 d, 01:01:01",
2947 );
2948 insta::assert_snapshot!(
2949 p(1.year().months(1).weeks(1).days(1).hours(1).minutes(1).seconds(1)),
2950 @"1 y, 1 mo, 1 w, 1 d, 01:01:01",
2951 );
2952 insta::assert_snapshot!(
2953 p(1.day().hours(1).minutes(1).seconds(1).nanoseconds(1)),
2954 @"1 d, 01:01:01.000000001",
2955 );
2956 }
2957
2958 #[test]
2959 fn print_span_hms_sign() {
2960 let printer = |direction| {
2961 SpanPrinter::new().hours_minutes_seconds(true).direction(direction)
2962 };
2963 let p = |direction, span| printer(direction).span_to_string(&span);
2964
2965 insta::assert_snapshot!(
2966 p(Direction::Auto, 1.hour()),
2967 @"01:00:00",
2968 );
2969 insta::assert_snapshot!(
2970 p(Direction::Sign, 1.hour()),
2971 @"01:00:00",
2972 );
2973 insta::assert_snapshot!(
2974 p(Direction::ForceSign, 1.hour()),
2975 @"+01:00:00",
2976 );
2977 insta::assert_snapshot!(
2978 p(Direction::Suffix, 1.hour()),
2979 @"01:00:00",
2980 );
2981 insta::assert_snapshot!(
2982 p(Direction::Auto, -1.hour()),
2983 @"-01:00:00",
2984 );
2985 insta::assert_snapshot!(
2986 p(Direction::Sign, -1.hour()),
2987 @"-01:00:00",
2988 );
2989 insta::assert_snapshot!(
2990 p(Direction::ForceSign, -1.hour()),
2991 @"-01:00:00",
2992 );
2993 insta::assert_snapshot!(
2994 p(Direction::Suffix, -1.hour()),
2995 @"01:00:00 ago",
2996 );
2997
2998 insta::assert_snapshot!(
2999 p(Direction::Auto, 1.day().hours(1)),
3000 @"1d 01:00:00",
3001 );
3002 insta::assert_snapshot!(
3003 p(Direction::Sign, 1.day().hours(1)),
3004 @"1d 01:00:00",
3005 );
3006 insta::assert_snapshot!(
3007 p(Direction::ForceSign, 1.day().hours(1)),
3008 @"+1d 01:00:00",
3009 );
3010 insta::assert_snapshot!(
3011 p(Direction::Suffix, 1.day().hours(1)),
3012 @"1d 01:00:00",
3013 );
3014 insta::assert_snapshot!(
3018 p(Direction::Auto, -1.day().hours(1)),
3019 @"1d 01:00:00 ago",
3020 );
3021 insta::assert_snapshot!(
3022 p(Direction::Sign, -1.day().hours(1)),
3023 @"-1d 01:00:00",
3024 );
3025 insta::assert_snapshot!(
3026 p(Direction::ForceSign, -1.day().hours(1)),
3027 @"-1d 01:00:00",
3028 );
3029 insta::assert_snapshot!(
3030 p(Direction::Suffix, -1.day().hours(1)),
3031 @"1d 01:00:00 ago",
3032 );
3033 }
3034
3035 #[test]
3036 fn print_span_hms_fraction_auto() {
3037 let printer = || SpanPrinter::new().hours_minutes_seconds(true);
3038 let p = |span| printer().span_to_string(&span);
3039
3040 insta::assert_snapshot!(p(1.nanosecond()), @"00:00:00.000000001");
3041 insta::assert_snapshot!(p(-1.nanosecond()), @"-00:00:00.000000001");
3042 insta::assert_snapshot!(
3043 printer().direction(Direction::ForceSign).span_to_string(&1.nanosecond()),
3044 @"+00:00:00.000000001",
3045 );
3046
3047 insta::assert_snapshot!(
3048 p(1.second().nanoseconds(123)),
3049 @"00:00:01.000000123",
3050 );
3051 insta::assert_snapshot!(
3052 p(1.second().milliseconds(123)),
3053 @"00:00:01.123",
3054 );
3055 insta::assert_snapshot!(
3056 p(1.second().milliseconds(1_123)),
3057 @"00:00:02.123",
3058 );
3059 insta::assert_snapshot!(
3060 p(1.second().milliseconds(61_123)),
3061 @"00:00:62.123",
3062 );
3063 }
3064
3065 #[test]
3066 fn print_span_hms_fraction_fixed_precision() {
3067 let printer = || SpanPrinter::new().hours_minutes_seconds(true);
3068 let p = |precision, span| {
3069 printer().precision(Some(precision)).span_to_string(&span)
3070 };
3071
3072 insta::assert_snapshot!(p(3, 1.second()), @"00:00:01.000");
3073 insta::assert_snapshot!(
3074 p(3, 1.second().milliseconds(1)),
3075 @"00:00:01.001",
3076 );
3077 insta::assert_snapshot!(
3078 p(3, 1.second().milliseconds(123)),
3079 @"00:00:01.123",
3080 );
3081 insta::assert_snapshot!(
3082 p(3, 1.second().milliseconds(100)),
3083 @"00:00:01.100",
3084 );
3085
3086 insta::assert_snapshot!(p(0, 1.second()), @"00:00:01");
3087 insta::assert_snapshot!(p(0, 1.second().milliseconds(1)), @"00:00:01");
3088 insta::assert_snapshot!(
3089 p(1, 1.second().milliseconds(999)),
3090 @"00:00:01.9",
3091 );
3092 }
3093
3094 #[test]
3095 fn print_duration_hms() {
3096 let printer = || SpanPrinter::new().hours_minutes_seconds(true);
3097 let p = |secs| {
3098 printer().duration_to_string(&SignedDuration::from_secs(secs))
3099 };
3100
3101 insta::assert_snapshot!(p(1), @"00:00:01");
3105 insta::assert_snapshot!(p(2), @"00:00:02");
3106 insta::assert_snapshot!(p(10), @"00:00:10");
3107 insta::assert_snapshot!(p(100), @"00:01:40");
3108
3109 insta::assert_snapshot!(p(1 * 60), @"00:01:00");
3110 insta::assert_snapshot!(p(2 * 60), @"00:02:00");
3111 insta::assert_snapshot!(p(10 * 60), @"00:10:00");
3112 insta::assert_snapshot!(p(100 * 60), @"01:40:00");
3113
3114 insta::assert_snapshot!(p(1 * 60 * 60), @"01:00:00");
3115 insta::assert_snapshot!(p(2 * 60 * 60), @"02:00:00");
3116 insta::assert_snapshot!(p(10 * 60 * 60), @"10:00:00");
3117 insta::assert_snapshot!(p(100 * 60 * 60), @"100:00:00");
3118
3119 insta::assert_snapshot!(
3120 p(60 * 60 + 60 + 1),
3121 @"01:01:01",
3122 );
3123 insta::assert_snapshot!(
3124 p(2 * 60 * 60 + 2 * 60 + 2),
3125 @"02:02:02",
3126 );
3127 insta::assert_snapshot!(
3128 p(10 * 60 * 60 + 10 * 60 + 10),
3129 @"10:10:10",
3130 );
3131 insta::assert_snapshot!(
3132 p(100 * 60 * 60 + 100 * 60 + 100),
3133 @"101:41:40",
3134 );
3135 }
3136
3137 #[test]
3138 fn print_duration_hms_sign() {
3139 let printer = |direction| {
3140 SpanPrinter::new().hours_minutes_seconds(true).direction(direction)
3141 };
3142 let p = |direction, secs| {
3143 printer(direction)
3144 .duration_to_string(&SignedDuration::from_secs(secs))
3145 };
3146
3147 insta::assert_snapshot!(p(Direction::Auto, 1), @"00:00:01");
3148 insta::assert_snapshot!(p(Direction::Sign, 1), @"00:00:01");
3149 insta::assert_snapshot!(p(Direction::ForceSign, 1), @"+00:00:01");
3150 insta::assert_snapshot!(p(Direction::Suffix, 1), @"00:00:01");
3151
3152 insta::assert_snapshot!(p(Direction::Auto, -1), @"-00:00:01");
3153 insta::assert_snapshot!(p(Direction::Sign, -1), @"-00:00:01");
3154 insta::assert_snapshot!(p(Direction::ForceSign, -1), @"-00:00:01");
3155 insta::assert_snapshot!(p(Direction::Suffix, -1), @"00:00:01 ago");
3156 }
3157
3158 #[test]
3159 fn print_duration_hms_fraction_auto() {
3160 let printer = || SpanPrinter::new().hours_minutes_seconds(true);
3161 let p = |secs, nanos| {
3162 printer().duration_to_string(&SignedDuration::new(secs, nanos))
3163 };
3164
3165 insta::assert_snapshot!(p(0, 1), @"00:00:00.000000001");
3166 insta::assert_snapshot!(p(0, -1), @"-00:00:00.000000001");
3167 insta::assert_snapshot!(
3168 printer().direction(Direction::ForceSign).duration_to_string(
3169 &SignedDuration::new(0, 1),
3170 ),
3171 @"+00:00:00.000000001",
3172 );
3173
3174 insta::assert_snapshot!(
3175 p(1, 123),
3176 @"00:00:01.000000123",
3177 );
3178 insta::assert_snapshot!(
3179 p(1, 123_000_000),
3180 @"00:00:01.123",
3181 );
3182 insta::assert_snapshot!(
3183 p(1, 1_123_000_000),
3184 @"00:00:02.123",
3185 );
3186 insta::assert_snapshot!(
3187 p(61, 1_123_000_000),
3188 @"00:01:02.123",
3189 );
3190 }
3191
3192 #[test]
3193 fn print_duration_hms_fraction_fixed_precision() {
3194 let printer = || SpanPrinter::new().hours_minutes_seconds(true);
3195 let p = |precision, secs, nanos| {
3196 printer()
3197 .precision(Some(precision))
3198 .duration_to_string(&SignedDuration::new(secs, nanos))
3199 };
3200
3201 insta::assert_snapshot!(p(3, 1, 0), @"00:00:01.000");
3202 insta::assert_snapshot!(
3203 p(3, 1, 1_000_000),
3204 @"00:00:01.001",
3205 );
3206 insta::assert_snapshot!(
3207 p(3, 1, 123_000_000),
3208 @"00:00:01.123",
3209 );
3210 insta::assert_snapshot!(
3211 p(3, 1, 100_000_000),
3212 @"00:00:01.100",
3213 );
3214
3215 insta::assert_snapshot!(p(0, 1, 0), @"00:00:01");
3216 insta::assert_snapshot!(p(0, 1, 1_000_000), @"00:00:01");
3217 insta::assert_snapshot!(
3218 p(1, 1, 999_000_000),
3219 @"00:00:01.9",
3220 );
3221 }
3222}