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(feature = "alloc")]
1733#[cfg(test)]
1734mod tests {
1735 use crate::ToSpan;
1736
1737 use super::*;
1738
1739 #[test]
1740 fn print_span_designator_default() {
1741 let printer = || SpanPrinter::new();
1742 let p = |span| printer().span_to_string(&span);
1743
1744 insta::assert_snapshot!(p(1.second()), @"1s");
1745 insta::assert_snapshot!(p(2.seconds()), @"2s");
1746 insta::assert_snapshot!(p(10.seconds()), @"10s");
1747 insta::assert_snapshot!(p(1.minute().seconds(40)), @"1m 40s");
1748
1749 insta::assert_snapshot!(p(1.minute()), @"1m");
1750 insta::assert_snapshot!(p(2.minutes()), @"2m");
1751 insta::assert_snapshot!(p(10.minutes()), @"10m");
1752 insta::assert_snapshot!(p(1.hour().minutes(40)), @"1h 40m");
1753
1754 insta::assert_snapshot!(p(1.hour()), @"1h");
1755 insta::assert_snapshot!(p(2.hours()), @"2h");
1756 insta::assert_snapshot!(p(10.hours()), @"10h");
1757 insta::assert_snapshot!(p(100.hours()), @"100h");
1758
1759 insta::assert_snapshot!(
1760 p(1.hour().minutes(1).seconds(1)),
1761 @"1h 1m 1s",
1762 );
1763 insta::assert_snapshot!(
1764 p(2.hours().minutes(2).seconds(2)),
1765 @"2h 2m 2s",
1766 );
1767 insta::assert_snapshot!(
1768 p(10.hours().minutes(10).seconds(10)),
1769 @"10h 10m 10s",
1770 );
1771 insta::assert_snapshot!(
1772 p(100.hours().minutes(100).seconds(100)),
1773 @"100h 100m 100s",
1774 );
1775
1776 insta::assert_snapshot!(p(-1.hour()), @"1h ago");
1777 insta::assert_snapshot!(p(-1.hour().seconds(30)), @"1h 30s ago");
1778
1779 insta::assert_snapshot!(
1780 p(1.second().milliseconds(2000)),
1781 @"1s 2000ms",
1782 );
1783 }
1784
1785 #[test]
1786 fn print_span_designator_verbose() {
1787 let printer = || SpanPrinter::new().designator(Designator::Verbose);
1788 let p = |span| printer().span_to_string(&span);
1789
1790 insta::assert_snapshot!(p(1.second()), @"1second");
1791 insta::assert_snapshot!(p(2.seconds()), @"2seconds");
1792 insta::assert_snapshot!(p(10.seconds()), @"10seconds");
1793 insta::assert_snapshot!(p(1.minute().seconds(40)), @"1minute 40seconds");
1794
1795 insta::assert_snapshot!(p(1.minute()), @"1minute");
1796 insta::assert_snapshot!(p(2.minutes()), @"2minutes");
1797 insta::assert_snapshot!(p(10.minutes()), @"10minutes");
1798 insta::assert_snapshot!(p(1.hour().minutes(40)), @"1hour 40minutes");
1799
1800 insta::assert_snapshot!(p(1.hour()), @"1hour");
1801 insta::assert_snapshot!(p(2.hours()), @"2hours");
1802 insta::assert_snapshot!(p(10.hours()), @"10hours");
1803 insta::assert_snapshot!(p(100.hours()), @"100hours");
1804
1805 insta::assert_snapshot!(
1806 p(1.hour().minutes(1).seconds(1)),
1807 @"1hour 1minute 1second",
1808 );
1809 insta::assert_snapshot!(
1810 p(2.hours().minutes(2).seconds(2)),
1811 @"2hours 2minutes 2seconds",
1812 );
1813 insta::assert_snapshot!(
1814 p(10.hours().minutes(10).seconds(10)),
1815 @"10hours 10minutes 10seconds",
1816 );
1817 insta::assert_snapshot!(
1818 p(100.hours().minutes(100).seconds(100)),
1819 @"100hours 100minutes 100seconds",
1820 );
1821
1822 insta::assert_snapshot!(p(-1.hour()), @"1hour ago");
1823 insta::assert_snapshot!(p(-1.hour().seconds(30)), @"1hour 30seconds ago");
1824 }
1825
1826 #[test]
1827 fn print_span_designator_short() {
1828 let printer = || SpanPrinter::new().designator(Designator::Short);
1829 let p = |span| printer().span_to_string(&span);
1830
1831 insta::assert_snapshot!(p(1.second()), @"1sec");
1832 insta::assert_snapshot!(p(2.seconds()), @"2secs");
1833 insta::assert_snapshot!(p(10.seconds()), @"10secs");
1834 insta::assert_snapshot!(p(1.minute().seconds(40)), @"1min 40secs");
1835
1836 insta::assert_snapshot!(p(1.minute()), @"1min");
1837 insta::assert_snapshot!(p(2.minutes()), @"2mins");
1838 insta::assert_snapshot!(p(10.minutes()), @"10mins");
1839 insta::assert_snapshot!(p(1.hour().minutes(40)), @"1hr 40mins");
1840
1841 insta::assert_snapshot!(p(1.hour()), @"1hr");
1842 insta::assert_snapshot!(p(2.hours()), @"2hrs");
1843 insta::assert_snapshot!(p(10.hours()), @"10hrs");
1844 insta::assert_snapshot!(p(100.hours()), @"100hrs");
1845
1846 insta::assert_snapshot!(
1847 p(1.hour().minutes(1).seconds(1)),
1848 @"1hr 1min 1sec",
1849 );
1850 insta::assert_snapshot!(
1851 p(2.hours().minutes(2).seconds(2)),
1852 @"2hrs 2mins 2secs",
1853 );
1854 insta::assert_snapshot!(
1855 p(10.hours().minutes(10).seconds(10)),
1856 @"10hrs 10mins 10secs",
1857 );
1858 insta::assert_snapshot!(
1859 p(100.hours().minutes(100).seconds(100)),
1860 @"100hrs 100mins 100secs",
1861 );
1862
1863 insta::assert_snapshot!(p(-1.hour()), @"1hr ago");
1864 insta::assert_snapshot!(p(-1.hour().seconds(30)), @"1hr 30secs ago");
1865 }
1866
1867 #[test]
1868 fn print_span_designator_compact() {
1869 let printer = || SpanPrinter::new().designator(Designator::Compact);
1870 let p = |span| printer().span_to_string(&span);
1871
1872 insta::assert_snapshot!(p(1.second()), @"1s");
1873 insta::assert_snapshot!(p(2.seconds()), @"2s");
1874 insta::assert_snapshot!(p(10.seconds()), @"10s");
1875 insta::assert_snapshot!(p(1.minute().seconds(40)), @"1m 40s");
1876
1877 insta::assert_snapshot!(p(1.minute()), @"1m");
1878 insta::assert_snapshot!(p(2.minutes()), @"2m");
1879 insta::assert_snapshot!(p(10.minutes()), @"10m");
1880 insta::assert_snapshot!(p(1.hour().minutes(40)), @"1h 40m");
1881
1882 insta::assert_snapshot!(p(1.hour()), @"1h");
1883 insta::assert_snapshot!(p(2.hours()), @"2h");
1884 insta::assert_snapshot!(p(10.hours()), @"10h");
1885 insta::assert_snapshot!(p(100.hours()), @"100h");
1886
1887 insta::assert_snapshot!(
1888 p(1.hour().minutes(1).seconds(1)),
1889 @"1h 1m 1s",
1890 );
1891 insta::assert_snapshot!(
1892 p(2.hours().minutes(2).seconds(2)),
1893 @"2h 2m 2s",
1894 );
1895 insta::assert_snapshot!(
1896 p(10.hours().minutes(10).seconds(10)),
1897 @"10h 10m 10s",
1898 );
1899 insta::assert_snapshot!(
1900 p(100.hours().minutes(100).seconds(100)),
1901 @"100h 100m 100s",
1902 );
1903
1904 insta::assert_snapshot!(p(-1.hour()), @"1h ago");
1905 insta::assert_snapshot!(p(-1.hour().seconds(30)), @"1h 30s ago");
1906 }
1907
1908 #[test]
1909 fn print_span_designator_direction_force() {
1910 let printer = || SpanPrinter::new().direction(Direction::ForceSign);
1911 let p = |span| printer().span_to_string(&span);
1912
1913 insta::assert_snapshot!(p(1.second()), @"+1s");
1914 insta::assert_snapshot!(p(2.seconds()), @"+2s");
1915 insta::assert_snapshot!(p(10.seconds()), @"+10s");
1916 insta::assert_snapshot!(p(1.minute().seconds(40)), @"+1m 40s");
1917
1918 insta::assert_snapshot!(p(1.minute()), @"+1m");
1919 insta::assert_snapshot!(p(2.minutes()), @"+2m");
1920 insta::assert_snapshot!(p(10.minutes()), @"+10m");
1921 insta::assert_snapshot!(p(1.hour().minutes(40)), @"+1h 40m");
1922
1923 insta::assert_snapshot!(p(1.hour()), @"+1h");
1924 insta::assert_snapshot!(p(2.hours()), @"+2h");
1925 insta::assert_snapshot!(p(10.hours()), @"+10h");
1926 insta::assert_snapshot!(p(100.hours()), @"+100h");
1927
1928 insta::assert_snapshot!(
1929 p(1.hour().minutes(1).seconds(1)),
1930 @"+1h 1m 1s",
1931 );
1932 insta::assert_snapshot!(
1933 p(2.hours().minutes(2).seconds(2)),
1934 @"+2h 2m 2s",
1935 );
1936 insta::assert_snapshot!(
1937 p(10.hours().minutes(10).seconds(10)),
1938 @"+10h 10m 10s",
1939 );
1940 insta::assert_snapshot!(
1941 p(100.hours().minutes(100).seconds(100)),
1942 @"+100h 100m 100s",
1943 );
1944
1945 insta::assert_snapshot!(p(-1.hour()), @"-1h");
1946 insta::assert_snapshot!(p(-1.hour().seconds(30)), @"-1h 30s");
1947 }
1948
1949 #[test]
1950 fn print_span_designator_padding() {
1951 let printer = || SpanPrinter::new().padding(2);
1952 let p = |span| printer().span_to_string(&span);
1953
1954 insta::assert_snapshot!(p(1.second()), @"01s");
1955 insta::assert_snapshot!(p(2.seconds()), @"02s");
1956 insta::assert_snapshot!(p(10.seconds()), @"10s");
1957 insta::assert_snapshot!(p(1.minute().seconds(40)), @"01m 40s");
1958
1959 insta::assert_snapshot!(p(1.minute()), @"01m");
1960 insta::assert_snapshot!(p(2.minutes()), @"02m");
1961 insta::assert_snapshot!(p(10.minutes()), @"10m");
1962 insta::assert_snapshot!(p(1.hour().minutes(40)), @"01h 40m");
1963
1964 insta::assert_snapshot!(p(1.hour()), @"01h");
1965 insta::assert_snapshot!(p(2.hours()), @"02h");
1966 insta::assert_snapshot!(p(10.hours()), @"10h");
1967 insta::assert_snapshot!(p(100.hours()), @"100h");
1968
1969 insta::assert_snapshot!(
1970 p(1.hour().minutes(1).seconds(1)),
1971 @"01h 01m 01s",
1972 );
1973 insta::assert_snapshot!(
1974 p(2.hours().minutes(2).seconds(2)),
1975 @"02h 02m 02s",
1976 );
1977 insta::assert_snapshot!(
1978 p(10.hours().minutes(10).seconds(10)),
1979 @"10h 10m 10s",
1980 );
1981 insta::assert_snapshot!(
1982 p(100.hours().minutes(100).seconds(100)),
1983 @"100h 100m 100s",
1984 );
1985
1986 insta::assert_snapshot!(p(-1.hour()), @"01h ago");
1987 insta::assert_snapshot!(p(-1.hour().seconds(30)), @"01h 30s ago");
1988 }
1989
1990 #[test]
1991 fn print_span_designator_spacing_none() {
1992 let printer = || SpanPrinter::new().spacing(Spacing::None);
1993 let p = |span| printer().span_to_string(&span);
1994
1995 insta::assert_snapshot!(p(1.second()), @"1s");
1996 insta::assert_snapshot!(p(2.seconds()), @"2s");
1997 insta::assert_snapshot!(p(10.seconds()), @"10s");
1998 insta::assert_snapshot!(p(1.minute().seconds(40)), @"1m40s");
1999
2000 insta::assert_snapshot!(p(1.minute()), @"1m");
2001 insta::assert_snapshot!(p(2.minutes()), @"2m");
2002 insta::assert_snapshot!(p(10.minutes()), @"10m");
2003 insta::assert_snapshot!(p(1.hour().minutes(40)), @"1h40m");
2004
2005 insta::assert_snapshot!(p(1.hour()), @"1h");
2006 insta::assert_snapshot!(p(2.hours()), @"2h");
2007 insta::assert_snapshot!(p(10.hours()), @"10h");
2008 insta::assert_snapshot!(p(100.hours()), @"100h");
2009
2010 insta::assert_snapshot!(
2011 p(1.hour().minutes(1).seconds(1)),
2012 @"1h1m1s",
2013 );
2014 insta::assert_snapshot!(
2015 p(2.hours().minutes(2).seconds(2)),
2016 @"2h2m2s",
2017 );
2018 insta::assert_snapshot!(
2019 p(10.hours().minutes(10).seconds(10)),
2020 @"10h10m10s",
2021 );
2022 insta::assert_snapshot!(
2023 p(100.hours().minutes(100).seconds(100)),
2024 @"100h100m100s",
2025 );
2026
2027 insta::assert_snapshot!(p(-1.hour()), @"-1h");
2028 insta::assert_snapshot!(p(-1.hour().seconds(30)), @"-1h30s");
2029 }
2030
2031 #[test]
2032 fn print_span_designator_spacing_more() {
2033 let printer =
2034 || SpanPrinter::new().spacing(Spacing::BetweenUnitsAndDesignators);
2035 let p = |span| printer().span_to_string(&span);
2036
2037 insta::assert_snapshot!(p(1.second()), @"1 s");
2038 insta::assert_snapshot!(p(2.seconds()), @"2 s");
2039 insta::assert_snapshot!(p(10.seconds()), @"10 s");
2040 insta::assert_snapshot!(p(1.minute().seconds(40)), @"1 m 40 s");
2041
2042 insta::assert_snapshot!(p(1.minute()), @"1 m");
2043 insta::assert_snapshot!(p(2.minutes()), @"2 m");
2044 insta::assert_snapshot!(p(10.minutes()), @"10 m");
2045 insta::assert_snapshot!(p(1.hour().minutes(40)), @"1 h 40 m");
2046
2047 insta::assert_snapshot!(p(1.hour()), @"1 h");
2048 insta::assert_snapshot!(p(2.hours()), @"2 h");
2049 insta::assert_snapshot!(p(10.hours()), @"10 h");
2050 insta::assert_snapshot!(p(100.hours()), @"100 h");
2051
2052 insta::assert_snapshot!(
2053 p(1.hour().minutes(1).seconds(1)),
2054 @"1 h 1 m 1 s",
2055 );
2056 insta::assert_snapshot!(
2057 p(2.hours().minutes(2).seconds(2)),
2058 @"2 h 2 m 2 s",
2059 );
2060 insta::assert_snapshot!(
2061 p(10.hours().minutes(10).seconds(10)),
2062 @"10 h 10 m 10 s",
2063 );
2064 insta::assert_snapshot!(
2065 p(100.hours().minutes(100).seconds(100)),
2066 @"100 h 100 m 100 s",
2067 );
2068
2069 insta::assert_snapshot!(p(-1.hour()), @"1 h ago");
2070 insta::assert_snapshot!(p(-1.hour().seconds(30)), @"1 h 30 s ago");
2071 }
2072
2073 #[test]
2074 fn print_span_designator_spacing_comma() {
2075 let printer = || {
2076 SpanPrinter::new()
2077 .comma_after_designator(true)
2078 .spacing(Spacing::BetweenUnitsAndDesignators)
2079 };
2080 let p = |span| printer().span_to_string(&span);
2081
2082 insta::assert_snapshot!(p(1.second()), @"1 s");
2083 insta::assert_snapshot!(p(2.seconds()), @"2 s");
2084 insta::assert_snapshot!(p(10.seconds()), @"10 s");
2085 insta::assert_snapshot!(p(1.minute().seconds(40)), @"1 m, 40 s");
2086
2087 insta::assert_snapshot!(p(1.minute()), @"1 m");
2088 insta::assert_snapshot!(p(2.minutes()), @"2 m");
2089 insta::assert_snapshot!(p(10.minutes()), @"10 m");
2090 insta::assert_snapshot!(p(1.hour().minutes(40)), @"1 h, 40 m");
2091
2092 insta::assert_snapshot!(p(1.hour()), @"1 h");
2093 insta::assert_snapshot!(p(2.hours()), @"2 h");
2094 insta::assert_snapshot!(p(10.hours()), @"10 h");
2095 insta::assert_snapshot!(p(100.hours()), @"100 h");
2096
2097 insta::assert_snapshot!(
2098 p(1.hour().minutes(1).seconds(1)),
2099 @"1 h, 1 m, 1 s",
2100 );
2101 insta::assert_snapshot!(
2102 p(2.hours().minutes(2).seconds(2)),
2103 @"2 h, 2 m, 2 s",
2104 );
2105 insta::assert_snapshot!(
2106 p(10.hours().minutes(10).seconds(10)),
2107 @"10 h, 10 m, 10 s",
2108 );
2109 insta::assert_snapshot!(
2110 p(100.hours().minutes(100).seconds(100)),
2111 @"100 h, 100 m, 100 s",
2112 );
2113
2114 insta::assert_snapshot!(p(-1.hour()), @"1 h ago");
2115 insta::assert_snapshot!(p(-1.hour().seconds(30)), @"1 h, 30 s ago");
2116 }
2117
2118 #[test]
2119 fn print_span_designator_fractional_hour() {
2120 let printer =
2121 || SpanPrinter::new().fractional(Some(FractionalUnit::Hour));
2122 let p = |span| printer().span_to_string(&span);
2123 let pp = |precision, span| {
2124 printer().precision(Some(precision)).span_to_string(&span)
2125 };
2126
2127 insta::assert_snapshot!(p(1.hour()), @"1h");
2128 insta::assert_snapshot!(pp(0, 1.hour()), @"1h");
2129 insta::assert_snapshot!(pp(1, 1.hour()), @"1.0h");
2130 insta::assert_snapshot!(pp(2, 1.hour()), @"1.00h");
2131
2132 insta::assert_snapshot!(p(1.hour().minutes(30)), @"1.5h");
2133 insta::assert_snapshot!(pp(0, 1.hour().minutes(30)), @"1h");
2134 insta::assert_snapshot!(pp(1, 1.hour().minutes(30)), @"1.5h");
2135 insta::assert_snapshot!(pp(2, 1.hour().minutes(30)), @"1.50h");
2136
2137 insta::assert_snapshot!(p(1.hour().minutes(3)), @"1.05h");
2138 insta::assert_snapshot!(p(1.hour().minutes(3).nanoseconds(1)), @"1.05h");
2139 insta::assert_snapshot!(p(1.second()), @"0.000277777h");
2140 insta::assert_snapshot!(p(1.second().nanoseconds(1)), @"0.000277777h");
2142 insta::assert_snapshot!(p(0.seconds()), @"0h");
2143 insta::assert_snapshot!(p(1.nanosecond()), @"0h");
2145 }
2146
2147 #[test]
2148 fn print_span_designator_fractional_minute() {
2149 let printer =
2150 || SpanPrinter::new().fractional(Some(FractionalUnit::Minute));
2151 let p = |span| printer().span_to_string(&span);
2152 let pp = |precision, span| {
2153 printer().precision(Some(precision)).span_to_string(&span)
2154 };
2155
2156 insta::assert_snapshot!(p(1.hour()), @"1h");
2157 insta::assert_snapshot!(p(1.hour().minutes(30)), @"1h 30m");
2158
2159 insta::assert_snapshot!(p(1.minute()), @"1m");
2160 insta::assert_snapshot!(pp(0, 1.minute()), @"1m");
2161 insta::assert_snapshot!(pp(1, 1.minute()), @"1.0m");
2162 insta::assert_snapshot!(pp(2, 1.minute()), @"1.00m");
2163
2164 insta::assert_snapshot!(p(1.minute().seconds(30)), @"1.5m");
2165 insta::assert_snapshot!(pp(0, 1.minute().seconds(30)), @"1m");
2166 insta::assert_snapshot!(pp(1, 1.minute().seconds(30)), @"1.5m");
2167 insta::assert_snapshot!(pp(2, 1.minute().seconds(30)), @"1.50m");
2168
2169 insta::assert_snapshot!(p(1.hour().nanoseconds(1)), @"1h");
2170 insta::assert_snapshot!(p(1.minute().seconds(3)), @"1.05m");
2171 insta::assert_snapshot!(p(1.minute().seconds(3).nanoseconds(1)), @"1.05m");
2172 insta::assert_snapshot!(p(1.second()), @"0.016666666m");
2173 insta::assert_snapshot!(p(1.second().nanoseconds(1)), @"0.016666666m");
2175 insta::assert_snapshot!(p(0.seconds()), @"0m");
2176 insta::assert_snapshot!(p(1.nanosecond()), @"0m");
2178 }
2179
2180 #[test]
2181 fn print_span_designator_fractional_second() {
2182 let printer =
2183 || SpanPrinter::new().fractional(Some(FractionalUnit::Second));
2184 let p = |span| printer().span_to_string(&span);
2185 let pp = |precision, span| {
2186 printer().precision(Some(precision)).span_to_string(&span)
2187 };
2188
2189 insta::assert_snapshot!(p(1.hour()), @"1h");
2190 insta::assert_snapshot!(p(1.hour().minutes(30)), @"1h 30m");
2191
2192 insta::assert_snapshot!(p(1.second()), @"1s");
2193 insta::assert_snapshot!(pp(0, 1.second()), @"1s");
2194 insta::assert_snapshot!(pp(1, 1.second()), @"1.0s");
2195 insta::assert_snapshot!(pp(2, 1.second()), @"1.00s");
2196
2197 insta::assert_snapshot!(p(1.second().milliseconds(500)), @"1.5s");
2198 insta::assert_snapshot!(pp(0, 1.second().milliseconds(500)), @"1s");
2199 insta::assert_snapshot!(pp(1, 1.second().milliseconds(500)), @"1.5s");
2200 insta::assert_snapshot!(pp(2, 1.second().milliseconds(500)), @"1.50s");
2201
2202 insta::assert_snapshot!(p(1.second().nanoseconds(1)), @"1.000000001s");
2203 insta::assert_snapshot!(p(1.nanosecond()), @"0.000000001s");
2204 insta::assert_snapshot!(p(0.seconds()), @"0s");
2205
2206 insta::assert_snapshot!(p(1.second().milliseconds(2000)), @"3s");
2207 }
2208
2209 #[test]
2210 fn print_span_designator_fractional_millisecond() {
2211 let printer = || {
2212 SpanPrinter::new().fractional(Some(FractionalUnit::Millisecond))
2213 };
2214 let p = |span| printer().span_to_string(&span);
2215 let pp = |precision, span| {
2216 printer().precision(Some(precision)).span_to_string(&span)
2217 };
2218
2219 insta::assert_snapshot!(p(1.hour()), @"1h");
2220 insta::assert_snapshot!(p(1.hour().minutes(30)), @"1h 30m");
2221 insta::assert_snapshot!(
2222 p(1.hour().minutes(30).seconds(10)),
2223 @"1h 30m 10s",
2224 );
2225
2226 insta::assert_snapshot!(p(1.second()), @"1s");
2227 insta::assert_snapshot!(pp(0, 1.second()), @"1s");
2228 insta::assert_snapshot!(pp(1, 1.second()), @"1s 0.0ms");
2229 insta::assert_snapshot!(pp(2, 1.second()), @"1s 0.00ms");
2230
2231 insta::assert_snapshot!(p(1.second().milliseconds(500)), @"1s 500ms");
2232 insta::assert_snapshot!(
2233 pp(0, 1.second().milliseconds(1).microseconds(500)),
2234 @"1s 1ms",
2235 );
2236 insta::assert_snapshot!(
2237 pp(1, 1.second().milliseconds(1).microseconds(500)),
2238 @"1s 1.5ms",
2239 );
2240 insta::assert_snapshot!(
2241 pp(2, 1.second().milliseconds(1).microseconds(500)),
2242 @"1s 1.50ms",
2243 );
2244
2245 insta::assert_snapshot!(p(1.millisecond().nanoseconds(1)), @"1.000001ms");
2246 insta::assert_snapshot!(p(1.nanosecond()), @"0.000001ms");
2247 insta::assert_snapshot!(p(0.seconds()), @"0ms");
2248 }
2249
2250 #[test]
2251 fn print_span_designator_fractional_microsecond() {
2252 let printer = || {
2253 SpanPrinter::new().fractional(Some(FractionalUnit::Microsecond))
2254 };
2255 let p = |span| printer().span_to_string(&span);
2256 let pp = |precision, span| {
2257 printer().precision(Some(precision)).span_to_string(&span)
2258 };
2259
2260 insta::assert_snapshot!(p(1.hour()), @"1h");
2261 insta::assert_snapshot!(p(1.hour().minutes(30)), @"1h 30m");
2262 insta::assert_snapshot!(
2263 p(1.hour().minutes(30).seconds(10)),
2264 @"1h 30m 10s",
2265 );
2266
2267 insta::assert_snapshot!(p(1.second()), @"1s");
2268 insta::assert_snapshot!(pp(0, 1.second()), @"1s");
2269 insta::assert_snapshot!(pp(1, 1.second()), @"1s 0.0µs");
2270 insta::assert_snapshot!(pp(2, 1.second()), @"1s 0.00µs");
2271
2272 insta::assert_snapshot!(p(1.second().milliseconds(500)), @"1s 500ms");
2273 insta::assert_snapshot!(
2274 pp(0, 1.second().milliseconds(1).microseconds(500)),
2275 @"1s 1ms 500µs",
2276 );
2277 insta::assert_snapshot!(
2278 pp(1, 1.second().milliseconds(1).microseconds(500)),
2279 @"1s 1ms 500.0µs",
2280 );
2281 insta::assert_snapshot!(
2282 pp(2, 1.second().milliseconds(1).microseconds(500)),
2283 @"1s 1ms 500.00µs",
2284 );
2285
2286 insta::assert_snapshot!(
2287 p(1.millisecond().nanoseconds(1)),
2288 @"1ms 0.001µs",
2289 );
2290 insta::assert_snapshot!(p(1.nanosecond()), @"0.001µs");
2291 insta::assert_snapshot!(p(0.second()), @"0µs");
2292 }
2293
2294 #[test]
2295 fn print_duration_designator_default() {
2296 let printer = || SpanPrinter::new();
2297 let p = |secs| {
2298 printer().duration_to_string(&SignedDuration::from_secs(secs))
2299 };
2300
2301 insta::assert_snapshot!(p(1), @"1s");
2302 insta::assert_snapshot!(p(2), @"2s");
2303 insta::assert_snapshot!(p(10), @"10s");
2304 insta::assert_snapshot!(p(100), @"1m 40s");
2305
2306 insta::assert_snapshot!(p(1 * 60), @"1m");
2307 insta::assert_snapshot!(p(2 * 60), @"2m");
2308 insta::assert_snapshot!(p(10 * 60), @"10m");
2309 insta::assert_snapshot!(p(100 * 60), @"1h 40m");
2310
2311 insta::assert_snapshot!(p(1 * 60 * 60), @"1h");
2312 insta::assert_snapshot!(p(2 * 60 * 60), @"2h");
2313 insta::assert_snapshot!(p(10 * 60 * 60), @"10h");
2314 insta::assert_snapshot!(p(100 * 60 * 60), @"100h");
2315
2316 insta::assert_snapshot!(
2317 p(60 * 60 + 60 + 1),
2318 @"1h 1m 1s",
2319 );
2320 insta::assert_snapshot!(
2321 p(2 * 60 * 60 + 2 * 60 + 2),
2322 @"2h 2m 2s",
2323 );
2324 insta::assert_snapshot!(
2325 p(10 * 60 * 60 + 10 * 60 + 10),
2326 @"10h 10m 10s",
2327 );
2328 insta::assert_snapshot!(
2329 p(100 * 60 * 60 + 100 * 60 + 100),
2330 @"101h 41m 40s",
2331 );
2332
2333 insta::assert_snapshot!(p(-1 * 60 * 60), @"1h ago");
2334 insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"1h 30s ago");
2335 }
2336
2337 #[test]
2338 fn print_duration_designator_verbose() {
2339 let printer = || SpanPrinter::new().designator(Designator::Verbose);
2340 let p = |secs| {
2341 printer().duration_to_string(&SignedDuration::from_secs(secs))
2342 };
2343
2344 insta::assert_snapshot!(p(1), @"1second");
2345 insta::assert_snapshot!(p(2), @"2seconds");
2346 insta::assert_snapshot!(p(10), @"10seconds");
2347 insta::assert_snapshot!(p(100), @"1minute 40seconds");
2348
2349 insta::assert_snapshot!(p(1 * 60), @"1minute");
2350 insta::assert_snapshot!(p(2 * 60), @"2minutes");
2351 insta::assert_snapshot!(p(10 * 60), @"10minutes");
2352 insta::assert_snapshot!(p(100 * 60), @"1hour 40minutes");
2353
2354 insta::assert_snapshot!(p(1 * 60 * 60), @"1hour");
2355 insta::assert_snapshot!(p(2 * 60 * 60), @"2hours");
2356 insta::assert_snapshot!(p(10 * 60 * 60), @"10hours");
2357 insta::assert_snapshot!(p(100 * 60 * 60), @"100hours");
2358
2359 insta::assert_snapshot!(
2360 p(60 * 60 + 60 + 1),
2361 @"1hour 1minute 1second",
2362 );
2363 insta::assert_snapshot!(
2364 p(2 * 60 * 60 + 2 * 60 + 2),
2365 @"2hours 2minutes 2seconds",
2366 );
2367 insta::assert_snapshot!(
2368 p(10 * 60 * 60 + 10 * 60 + 10),
2369 @"10hours 10minutes 10seconds",
2370 );
2371 insta::assert_snapshot!(
2372 p(100 * 60 * 60 + 100 * 60 + 100),
2373 @"101hours 41minutes 40seconds",
2374 );
2375
2376 insta::assert_snapshot!(p(-1 * 60 * 60), @"1hour ago");
2377 insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"1hour 30seconds ago");
2378 }
2379
2380 #[test]
2381 fn print_duration_designator_short() {
2382 let printer = || SpanPrinter::new().designator(Designator::Short);
2383 let p = |secs| {
2384 printer().duration_to_string(&SignedDuration::from_secs(secs))
2385 };
2386
2387 insta::assert_snapshot!(p(1), @"1sec");
2388 insta::assert_snapshot!(p(2), @"2secs");
2389 insta::assert_snapshot!(p(10), @"10secs");
2390 insta::assert_snapshot!(p(100), @"1min 40secs");
2391
2392 insta::assert_snapshot!(p(1 * 60), @"1min");
2393 insta::assert_snapshot!(p(2 * 60), @"2mins");
2394 insta::assert_snapshot!(p(10 * 60), @"10mins");
2395 insta::assert_snapshot!(p(100 * 60), @"1hr 40mins");
2396
2397 insta::assert_snapshot!(p(1 * 60 * 60), @"1hr");
2398 insta::assert_snapshot!(p(2 * 60 * 60), @"2hrs");
2399 insta::assert_snapshot!(p(10 * 60 * 60), @"10hrs");
2400 insta::assert_snapshot!(p(100 * 60 * 60), @"100hrs");
2401
2402 insta::assert_snapshot!(
2403 p(60 * 60 + 60 + 1),
2404 @"1hr 1min 1sec",
2405 );
2406 insta::assert_snapshot!(
2407 p(2 * 60 * 60 + 2 * 60 + 2),
2408 @"2hrs 2mins 2secs",
2409 );
2410 insta::assert_snapshot!(
2411 p(10 * 60 * 60 + 10 * 60 + 10),
2412 @"10hrs 10mins 10secs",
2413 );
2414 insta::assert_snapshot!(
2415 p(100 * 60 * 60 + 100 * 60 + 100),
2416 @"101hrs 41mins 40secs",
2417 );
2418
2419 insta::assert_snapshot!(p(-1 * 60 * 60), @"1hr ago");
2420 insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"1hr 30secs ago");
2421 }
2422
2423 #[test]
2424 fn print_duration_designator_compact() {
2425 let printer = || SpanPrinter::new().designator(Designator::Compact);
2426 let p = |secs| {
2427 printer().duration_to_string(&SignedDuration::from_secs(secs))
2428 };
2429
2430 insta::assert_snapshot!(p(1), @"1s");
2431 insta::assert_snapshot!(p(2), @"2s");
2432 insta::assert_snapshot!(p(10), @"10s");
2433 insta::assert_snapshot!(p(100), @"1m 40s");
2434
2435 insta::assert_snapshot!(p(1 * 60), @"1m");
2436 insta::assert_snapshot!(p(2 * 60), @"2m");
2437 insta::assert_snapshot!(p(10 * 60), @"10m");
2438 insta::assert_snapshot!(p(100 * 60), @"1h 40m");
2439
2440 insta::assert_snapshot!(p(1 * 60 * 60), @"1h");
2441 insta::assert_snapshot!(p(2 * 60 * 60), @"2h");
2442 insta::assert_snapshot!(p(10 * 60 * 60), @"10h");
2443 insta::assert_snapshot!(p(100 * 60 * 60), @"100h");
2444
2445 insta::assert_snapshot!(
2446 p(60 * 60 + 60 + 1),
2447 @"1h 1m 1s",
2448 );
2449 insta::assert_snapshot!(
2450 p(2 * 60 * 60 + 2 * 60 + 2),
2451 @"2h 2m 2s",
2452 );
2453 insta::assert_snapshot!(
2454 p(10 * 60 * 60 + 10 * 60 + 10),
2455 @"10h 10m 10s",
2456 );
2457 insta::assert_snapshot!(
2458 p(100 * 60 * 60 + 100 * 60 + 100),
2459 @"101h 41m 40s",
2460 );
2461
2462 insta::assert_snapshot!(p(-1 * 60 * 60), @"1h ago");
2463 insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"1h 30s ago");
2464 }
2465
2466 #[test]
2467 fn print_duration_designator_direction_force() {
2468 let printer = || SpanPrinter::new().direction(Direction::ForceSign);
2469 let p = |secs| {
2470 printer().duration_to_string(&SignedDuration::from_secs(secs))
2471 };
2472
2473 insta::assert_snapshot!(p(1), @"+1s");
2474 insta::assert_snapshot!(p(2), @"+2s");
2475 insta::assert_snapshot!(p(10), @"+10s");
2476 insta::assert_snapshot!(p(100), @"+1m 40s");
2477
2478 insta::assert_snapshot!(p(1 * 60), @"+1m");
2479 insta::assert_snapshot!(p(2 * 60), @"+2m");
2480 insta::assert_snapshot!(p(10 * 60), @"+10m");
2481 insta::assert_snapshot!(p(100 * 60), @"+1h 40m");
2482
2483 insta::assert_snapshot!(p(1 * 60 * 60), @"+1h");
2484 insta::assert_snapshot!(p(2 * 60 * 60), @"+2h");
2485 insta::assert_snapshot!(p(10 * 60 * 60), @"+10h");
2486 insta::assert_snapshot!(p(100 * 60 * 60), @"+100h");
2487
2488 insta::assert_snapshot!(
2489 p(60 * 60 + 60 + 1),
2490 @"+1h 1m 1s",
2491 );
2492 insta::assert_snapshot!(
2493 p(2 * 60 * 60 + 2 * 60 + 2),
2494 @"+2h 2m 2s",
2495 );
2496 insta::assert_snapshot!(
2497 p(10 * 60 * 60 + 10 * 60 + 10),
2498 @"+10h 10m 10s",
2499 );
2500 insta::assert_snapshot!(
2501 p(100 * 60 * 60 + 100 * 60 + 100),
2502 @"+101h 41m 40s",
2503 );
2504
2505 insta::assert_snapshot!(p(-1 * 60 * 60), @"-1h");
2506 insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"-1h 30s");
2507 }
2508
2509 #[test]
2510 fn print_duration_designator_padding() {
2511 let printer = || SpanPrinter::new().padding(2);
2512 let p = |secs| {
2513 printer().duration_to_string(&SignedDuration::from_secs(secs))
2514 };
2515
2516 insta::assert_snapshot!(p(1), @"01s");
2517 insta::assert_snapshot!(p(2), @"02s");
2518 insta::assert_snapshot!(p(10), @"10s");
2519 insta::assert_snapshot!(p(100), @"01m 40s");
2520
2521 insta::assert_snapshot!(p(1 * 60), @"01m");
2522 insta::assert_snapshot!(p(2 * 60), @"02m");
2523 insta::assert_snapshot!(p(10 * 60), @"10m");
2524 insta::assert_snapshot!(p(100 * 60), @"01h 40m");
2525
2526 insta::assert_snapshot!(p(1 * 60 * 60), @"01h");
2527 insta::assert_snapshot!(p(2 * 60 * 60), @"02h");
2528 insta::assert_snapshot!(p(10 * 60 * 60), @"10h");
2529 insta::assert_snapshot!(p(100 * 60 * 60), @"100h");
2530
2531 insta::assert_snapshot!(
2532 p(60 * 60 + 60 + 1),
2533 @"01h 01m 01s",
2534 );
2535 insta::assert_snapshot!(
2536 p(2 * 60 * 60 + 2 * 60 + 2),
2537 @"02h 02m 02s",
2538 );
2539 insta::assert_snapshot!(
2540 p(10 * 60 * 60 + 10 * 60 + 10),
2541 @"10h 10m 10s",
2542 );
2543 insta::assert_snapshot!(
2544 p(100 * 60 * 60 + 100 * 60 + 100),
2545 @"101h 41m 40s",
2546 );
2547
2548 insta::assert_snapshot!(p(-1 * 60 * 60), @"01h ago");
2549 insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"01h 30s ago");
2550 }
2551
2552 #[test]
2553 fn print_duration_designator_spacing_none() {
2554 let printer = || SpanPrinter::new().spacing(Spacing::None);
2555 let p = |secs| {
2556 printer().duration_to_string(&SignedDuration::from_secs(secs))
2557 };
2558
2559 insta::assert_snapshot!(p(1), @"1s");
2560 insta::assert_snapshot!(p(2), @"2s");
2561 insta::assert_snapshot!(p(10), @"10s");
2562 insta::assert_snapshot!(p(100), @"1m40s");
2563
2564 insta::assert_snapshot!(p(1 * 60), @"1m");
2565 insta::assert_snapshot!(p(2 * 60), @"2m");
2566 insta::assert_snapshot!(p(10 * 60), @"10m");
2567 insta::assert_snapshot!(p(100 * 60), @"1h40m");
2568
2569 insta::assert_snapshot!(p(1 * 60 * 60), @"1h");
2570 insta::assert_snapshot!(p(2 * 60 * 60), @"2h");
2571 insta::assert_snapshot!(p(10 * 60 * 60), @"10h");
2572 insta::assert_snapshot!(p(100 * 60 * 60), @"100h");
2573
2574 insta::assert_snapshot!(
2575 p(60 * 60 + 60 + 1),
2576 @"1h1m1s",
2577 );
2578 insta::assert_snapshot!(
2579 p(2 * 60 * 60 + 2 * 60 + 2),
2580 @"2h2m2s",
2581 );
2582 insta::assert_snapshot!(
2583 p(10 * 60 * 60 + 10 * 60 + 10),
2584 @"10h10m10s",
2585 );
2586 insta::assert_snapshot!(
2587 p(100 * 60 * 60 + 100 * 60 + 100),
2588 @"101h41m40s",
2589 );
2590
2591 insta::assert_snapshot!(p(-1 * 60 * 60), @"-1h");
2592 insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"-1h30s");
2593 }
2594
2595 #[test]
2596 fn print_duration_designator_spacing_more() {
2597 let printer =
2598 || SpanPrinter::new().spacing(Spacing::BetweenUnitsAndDesignators);
2599 let p = |secs| {
2600 printer().duration_to_string(&SignedDuration::from_secs(secs))
2601 };
2602
2603 insta::assert_snapshot!(p(1), @"1 s");
2604 insta::assert_snapshot!(p(2), @"2 s");
2605 insta::assert_snapshot!(p(10), @"10 s");
2606 insta::assert_snapshot!(p(100), @"1 m 40 s");
2607
2608 insta::assert_snapshot!(p(1 * 60), @"1 m");
2609 insta::assert_snapshot!(p(2 * 60), @"2 m");
2610 insta::assert_snapshot!(p(10 * 60), @"10 m");
2611 insta::assert_snapshot!(p(100 * 60), @"1 h 40 m");
2612
2613 insta::assert_snapshot!(p(1 * 60 * 60), @"1 h");
2614 insta::assert_snapshot!(p(2 * 60 * 60), @"2 h");
2615 insta::assert_snapshot!(p(10 * 60 * 60), @"10 h");
2616 insta::assert_snapshot!(p(100 * 60 * 60), @"100 h");
2617
2618 insta::assert_snapshot!(
2619 p(60 * 60 + 60 + 1),
2620 @"1 h 1 m 1 s",
2621 );
2622 insta::assert_snapshot!(
2623 p(2 * 60 * 60 + 2 * 60 + 2),
2624 @"2 h 2 m 2 s",
2625 );
2626 insta::assert_snapshot!(
2627 p(10 * 60 * 60 + 10 * 60 + 10),
2628 @"10 h 10 m 10 s",
2629 );
2630 insta::assert_snapshot!(
2631 p(100 * 60 * 60 + 100 * 60 + 100),
2632 @"101 h 41 m 40 s",
2633 );
2634
2635 insta::assert_snapshot!(p(-1 * 60 * 60), @"1 h ago");
2636 insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"1 h 30 s ago");
2637 }
2638
2639 #[test]
2640 fn print_duration_designator_spacing_comma() {
2641 let printer = || {
2642 SpanPrinter::new()
2643 .comma_after_designator(true)
2644 .spacing(Spacing::BetweenUnitsAndDesignators)
2645 };
2646 let p = |secs| {
2647 printer().duration_to_string(&SignedDuration::from_secs(secs))
2648 };
2649
2650 insta::assert_snapshot!(p(1), @"1 s");
2651 insta::assert_snapshot!(p(2), @"2 s");
2652 insta::assert_snapshot!(p(10), @"10 s");
2653 insta::assert_snapshot!(p(100), @"1 m, 40 s");
2654
2655 insta::assert_snapshot!(p(1 * 60), @"1 m");
2656 insta::assert_snapshot!(p(2 * 60), @"2 m");
2657 insta::assert_snapshot!(p(10 * 60), @"10 m");
2658 insta::assert_snapshot!(p(100 * 60), @"1 h, 40 m");
2659
2660 insta::assert_snapshot!(p(1 * 60 * 60), @"1 h");
2661 insta::assert_snapshot!(p(2 * 60 * 60), @"2 h");
2662 insta::assert_snapshot!(p(10 * 60 * 60), @"10 h");
2663 insta::assert_snapshot!(p(100 * 60 * 60), @"100 h");
2664
2665 insta::assert_snapshot!(
2666 p(60 * 60 + 60 + 1),
2667 @"1 h, 1 m, 1 s",
2668 );
2669 insta::assert_snapshot!(
2670 p(2 * 60 * 60 + 2 * 60 + 2),
2671 @"2 h, 2 m, 2 s",
2672 );
2673 insta::assert_snapshot!(
2674 p(10 * 60 * 60 + 10 * 60 + 10),
2675 @"10 h, 10 m, 10 s",
2676 );
2677 insta::assert_snapshot!(
2678 p(100 * 60 * 60 + 100 * 60 + 100),
2679 @"101 h, 41 m, 40 s",
2680 );
2681
2682 insta::assert_snapshot!(p(-1 * 60 * 60), @"1 h ago");
2683 insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"1 h, 30 s ago");
2684 }
2685
2686 #[test]
2687 fn print_duration_designator_fractional_hour() {
2688 let printer =
2689 || SpanPrinter::new().fractional(Some(FractionalUnit::Hour));
2690 let p = |secs, nanos| {
2691 printer().duration_to_string(&SignedDuration::new(secs, nanos))
2692 };
2693 let pp = |precision, secs, nanos| {
2694 printer()
2695 .precision(Some(precision))
2696 .duration_to_string(&SignedDuration::new(secs, nanos))
2697 };
2698
2699 insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
2700 insta::assert_snapshot!(pp(0, 1 * 60 * 60, 0), @"1h");
2701 insta::assert_snapshot!(pp(1, 1 * 60 * 60, 0), @"1.0h");
2702 insta::assert_snapshot!(pp(2, 1 * 60 * 60, 0), @"1.00h");
2703
2704 insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1.5h");
2705 insta::assert_snapshot!(pp(0, 1 * 60 * 60 + 30 * 60, 0), @"1h");
2706 insta::assert_snapshot!(pp(1, 1 * 60 * 60 + 30 * 60, 0), @"1.5h");
2707 insta::assert_snapshot!(pp(2, 1 * 60 * 60 + 30 * 60, 0), @"1.50h");
2708
2709 insta::assert_snapshot!(p(1 * 60 * 60 + 3 * 60, 0), @"1.05h");
2710 insta::assert_snapshot!(p(1 * 60 * 60 + 3 * 60, 1), @"1.05h");
2711 insta::assert_snapshot!(p(1, 0), @"0.000277777h");
2712 insta::assert_snapshot!(p(1, 1), @"0.000277777h");
2714 insta::assert_snapshot!(p(0, 0), @"0h");
2715 insta::assert_snapshot!(p(0, 1), @"0h");
2717
2718 insta::assert_snapshot!(
2719 printer().duration_to_string(&SignedDuration::MIN),
2720 @"2562047788015215.502499999h ago",
2721 );
2722 }
2723
2724 #[test]
2725 fn print_duration_designator_fractional_minute() {
2726 let printer =
2727 || SpanPrinter::new().fractional(Some(FractionalUnit::Minute));
2728 let p = |secs, nanos| {
2729 printer().duration_to_string(&SignedDuration::new(secs, nanos))
2730 };
2731 let pp = |precision, secs, nanos| {
2732 printer()
2733 .precision(Some(precision))
2734 .duration_to_string(&SignedDuration::new(secs, nanos))
2735 };
2736
2737 insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
2738 insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1h 30m");
2739
2740 insta::assert_snapshot!(p(60, 0), @"1m");
2741 insta::assert_snapshot!(pp(0, 60, 0), @"1m");
2742 insta::assert_snapshot!(pp(1, 60, 0), @"1.0m");
2743 insta::assert_snapshot!(pp(2, 60, 0), @"1.00m");
2744
2745 insta::assert_snapshot!(p(90, 0), @"1.5m");
2746 insta::assert_snapshot!(pp(0, 90, 0), @"1m");
2747 insta::assert_snapshot!(pp(1, 90, 0), @"1.5m");
2748 insta::assert_snapshot!(pp(2, 90, 0), @"1.50m");
2749
2750 insta::assert_snapshot!(p(1 * 60 * 60, 1), @"1h");
2751 insta::assert_snapshot!(p(63, 0), @"1.05m");
2752 insta::assert_snapshot!(p(63, 1), @"1.05m");
2753 insta::assert_snapshot!(p(1, 0), @"0.016666666m");
2754 insta::assert_snapshot!(p(1, 1), @"0.016666666m");
2756 insta::assert_snapshot!(p(0, 0), @"0m");
2757 insta::assert_snapshot!(p(0, 1), @"0m");
2759
2760 insta::assert_snapshot!(
2761 printer().duration_to_string(&SignedDuration::MIN),
2762 @"2562047788015215h 30.149999999m ago",
2763 );
2764 }
2765
2766 #[test]
2767 fn print_duration_designator_fractional_second() {
2768 let printer =
2769 || SpanPrinter::new().fractional(Some(FractionalUnit::Second));
2770 let p = |secs, nanos| {
2771 printer().duration_to_string(&SignedDuration::new(secs, nanos))
2772 };
2773 let pp = |precision, secs, nanos| {
2774 printer()
2775 .precision(Some(precision))
2776 .duration_to_string(&SignedDuration::new(secs, nanos))
2777 };
2778
2779 insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
2780 insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1h 30m");
2781
2782 insta::assert_snapshot!(p(1, 0), @"1s");
2783 insta::assert_snapshot!(pp(0, 1, 0), @"1s");
2784 insta::assert_snapshot!(pp(1, 1, 0), @"1.0s");
2785 insta::assert_snapshot!(pp(2, 1, 0), @"1.00s");
2786
2787 insta::assert_snapshot!(p(1, 500_000_000), @"1.5s");
2788 insta::assert_snapshot!(pp(0, 1, 500_000_000), @"1s");
2789 insta::assert_snapshot!(pp(1, 1, 500_000_000), @"1.5s");
2790 insta::assert_snapshot!(pp(2, 1, 500_000_000), @"1.50s");
2791
2792 insta::assert_snapshot!(p(1, 1), @"1.000000001s");
2793 insta::assert_snapshot!(p(0, 1), @"0.000000001s");
2794 insta::assert_snapshot!(p(0, 0), @"0s");
2795
2796 insta::assert_snapshot!(
2797 printer().duration_to_string(&SignedDuration::MIN),
2798 @"2562047788015215h 30m 8.999999999s ago",
2799 );
2800 }
2801
2802 #[test]
2803 fn print_duration_designator_fractional_millisecond() {
2804 let printer = || {
2805 SpanPrinter::new().fractional(Some(FractionalUnit::Millisecond))
2806 };
2807 let p = |secs, nanos| {
2808 printer().duration_to_string(&SignedDuration::new(secs, nanos))
2809 };
2810 let pp = |precision, secs, nanos| {
2811 printer()
2812 .precision(Some(precision))
2813 .duration_to_string(&SignedDuration::new(secs, nanos))
2814 };
2815
2816 insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
2817 insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1h 30m");
2818 insta::assert_snapshot!(
2819 p(1 * 60 * 60 + 30 * 60 + 10, 0),
2820 @"1h 30m 10s",
2821 );
2822
2823 insta::assert_snapshot!(p(1, 0), @"1s");
2824 insta::assert_snapshot!(pp(0, 1, 0), @"1s");
2825 insta::assert_snapshot!(pp(1, 1, 0), @"1s 0.0ms");
2826 insta::assert_snapshot!(pp(2, 1, 0), @"1s 0.00ms");
2827
2828 insta::assert_snapshot!(p(1, 500_000_000), @"1s 500ms");
2829 insta::assert_snapshot!(pp(0, 1, 1_500_000), @"1s 1ms");
2830 insta::assert_snapshot!(pp(1, 1, 1_500_000), @"1s 1.5ms");
2831 insta::assert_snapshot!(pp(2, 1, 1_500_000), @"1s 1.50ms");
2832
2833 insta::assert_snapshot!(p(0, 1_000_001), @"1.000001ms");
2834 insta::assert_snapshot!(p(0, 0_000_001), @"0.000001ms");
2835 insta::assert_snapshot!(p(0, 0), @"0ms");
2836
2837 insta::assert_snapshot!(
2838 printer().duration_to_string(&SignedDuration::MIN),
2839 @"2562047788015215h 30m 8s 999.999999ms ago",
2840 );
2841 }
2842
2843 #[test]
2844 fn print_duration_designator_fractional_microsecond() {
2845 let printer = || {
2846 SpanPrinter::new().fractional(Some(FractionalUnit::Microsecond))
2847 };
2848 let p = |secs, nanos| {
2849 printer().duration_to_string(&SignedDuration::new(secs, nanos))
2850 };
2851 let pp = |precision, secs, nanos| {
2852 printer()
2853 .precision(Some(precision))
2854 .duration_to_string(&SignedDuration::new(secs, nanos))
2855 };
2856
2857 insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
2858 insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1h 30m");
2859 insta::assert_snapshot!(
2860 p(1 * 60 * 60 + 30 * 60 + 10, 0),
2861 @"1h 30m 10s",
2862 );
2863
2864 insta::assert_snapshot!(p(1, 0), @"1s");
2865 insta::assert_snapshot!(pp(0, 1, 0), @"1s");
2866 insta::assert_snapshot!(pp(1, 1, 0), @"1s 0.0µs");
2867 insta::assert_snapshot!(pp(2, 1, 0), @"1s 0.00µs");
2868
2869 insta::assert_snapshot!(p(1, 500_000_000), @"1s 500ms");
2870 insta::assert_snapshot!(pp(0, 1, 1_500_000), @"1s 1ms 500µs");
2871 insta::assert_snapshot!(pp(1, 1, 1_500_000), @"1s 1ms 500.0µs");
2872 insta::assert_snapshot!(pp(2, 1, 1_500_000), @"1s 1ms 500.00µs");
2873
2874 insta::assert_snapshot!(p(0, 1_000_001), @"1ms 0.001µs");
2875 insta::assert_snapshot!(p(0, 0_000_001), @"0.001µs");
2876 insta::assert_snapshot!(p(0, 0), @"0µs");
2877
2878 insta::assert_snapshot!(
2879 printer().duration_to_string(&SignedDuration::MIN),
2880 @"2562047788015215h 30m 8s 999ms 999.999µs ago",
2881 );
2882 }
2883
2884 #[test]
2885 fn print_span_hms() {
2886 let printer = || SpanPrinter::new().hours_minutes_seconds(true);
2887 let p = |span| printer().span_to_string(&span);
2888
2889 insta::assert_snapshot!(p(1.second()), @"00:00:01");
2890 insta::assert_snapshot!(p(2.seconds()), @"00:00:02");
2891 insta::assert_snapshot!(p(10.seconds()), @"00:00:10");
2892 insta::assert_snapshot!(p(100.seconds()), @"00:00:100");
2893
2894 insta::assert_snapshot!(p(1.minute()), @"00:01:00");
2895 insta::assert_snapshot!(p(2.minutes()), @"00:02:00");
2896 insta::assert_snapshot!(p(10.minutes()), @"00:10:00");
2897 insta::assert_snapshot!(p(100.minutes()), @"00:100:00");
2898
2899 insta::assert_snapshot!(p(1.hour()), @"01:00:00");
2900 insta::assert_snapshot!(p(2.hours()), @"02:00:00");
2901 insta::assert_snapshot!(p(10.hours()), @"10:00:00");
2902 insta::assert_snapshot!(p(100.hours()), @"100:00:00");
2903
2904 insta::assert_snapshot!(
2905 p(1.hour().minutes(1).seconds(1)),
2906 @"01:01:01",
2907 );
2908 insta::assert_snapshot!(
2909 p(2.hours().minutes(2).seconds(2)),
2910 @"02:02:02",
2911 );
2912 insta::assert_snapshot!(
2913 p(10.hours().minutes(10).seconds(10)),
2914 @"10:10:10",
2915 );
2916 insta::assert_snapshot!(
2917 p(100.hours().minutes(100).seconds(100)),
2918 @"100:100:100",
2919 );
2920
2921 insta::assert_snapshot!(
2922 p(1.day().hours(1).minutes(1).seconds(1)),
2923 @"1d 01:01:01",
2924 );
2925 insta::assert_snapshot!(
2926 p(1.day()),
2927 @"1d 00:00:00",
2928 );
2929 insta::assert_snapshot!(
2930 p(1.day().seconds(2)),
2931 @"1d 00:00:02",
2932 );
2933 }
2934
2935 #[test]
2936 fn print_span_hms_fmt() {
2937 let printer = || {
2938 SpanPrinter::new()
2939 .hours_minutes_seconds(true)
2940 .comma_after_designator(true)
2941 .spacing(Spacing::BetweenUnitsAndDesignators)
2942 };
2943 let p = |span| printer().span_to_string(&span);
2944
2945 insta::assert_snapshot!(
2946 p(1.day().hours(1).minutes(1).seconds(1)),
2947 @"1 d, 01:01:01",
2948 );
2949 insta::assert_snapshot!(
2950 p(1.year().months(1).weeks(1).days(1).hours(1).minutes(1).seconds(1)),
2951 @"1 y, 1 mo, 1 w, 1 d, 01:01:01",
2952 );
2953 insta::assert_snapshot!(
2954 p(1.day().hours(1).minutes(1).seconds(1).nanoseconds(1)),
2955 @"1 d, 01:01:01.000000001",
2956 );
2957 }
2958
2959 #[test]
2960 fn print_span_hms_sign() {
2961 let printer = |direction| {
2962 SpanPrinter::new().hours_minutes_seconds(true).direction(direction)
2963 };
2964 let p = |direction, span| printer(direction).span_to_string(&span);
2965
2966 insta::assert_snapshot!(
2967 p(Direction::Auto, 1.hour()),
2968 @"01:00:00",
2969 );
2970 insta::assert_snapshot!(
2971 p(Direction::Sign, 1.hour()),
2972 @"01:00:00",
2973 );
2974 insta::assert_snapshot!(
2975 p(Direction::ForceSign, 1.hour()),
2976 @"+01:00:00",
2977 );
2978 insta::assert_snapshot!(
2979 p(Direction::Suffix, 1.hour()),
2980 @"01:00:00",
2981 );
2982 insta::assert_snapshot!(
2983 p(Direction::Auto, -1.hour()),
2984 @"-01:00:00",
2985 );
2986 insta::assert_snapshot!(
2987 p(Direction::Sign, -1.hour()),
2988 @"-01:00:00",
2989 );
2990 insta::assert_snapshot!(
2991 p(Direction::ForceSign, -1.hour()),
2992 @"-01:00:00",
2993 );
2994 insta::assert_snapshot!(
2995 p(Direction::Suffix, -1.hour()),
2996 @"01:00:00 ago",
2997 );
2998
2999 insta::assert_snapshot!(
3000 p(Direction::Auto, 1.day().hours(1)),
3001 @"1d 01:00:00",
3002 );
3003 insta::assert_snapshot!(
3004 p(Direction::Sign, 1.day().hours(1)),
3005 @"1d 01:00:00",
3006 );
3007 insta::assert_snapshot!(
3008 p(Direction::ForceSign, 1.day().hours(1)),
3009 @"+1d 01:00:00",
3010 );
3011 insta::assert_snapshot!(
3012 p(Direction::Suffix, 1.day().hours(1)),
3013 @"1d 01:00:00",
3014 );
3015 insta::assert_snapshot!(
3019 p(Direction::Auto, -1.day().hours(1)),
3020 @"1d 01:00:00 ago",
3021 );
3022 insta::assert_snapshot!(
3023 p(Direction::Sign, -1.day().hours(1)),
3024 @"-1d 01:00:00",
3025 );
3026 insta::assert_snapshot!(
3027 p(Direction::ForceSign, -1.day().hours(1)),
3028 @"-1d 01:00:00",
3029 );
3030 insta::assert_snapshot!(
3031 p(Direction::Suffix, -1.day().hours(1)),
3032 @"1d 01:00:00 ago",
3033 );
3034 }
3035
3036 #[test]
3037 fn print_span_hms_fraction_auto() {
3038 let printer = || SpanPrinter::new().hours_minutes_seconds(true);
3039 let p = |span| printer().span_to_string(&span);
3040
3041 insta::assert_snapshot!(p(1.nanosecond()), @"00:00:00.000000001");
3042 insta::assert_snapshot!(p(-1.nanosecond()), @"-00:00:00.000000001");
3043 insta::assert_snapshot!(
3044 printer().direction(Direction::ForceSign).span_to_string(&1.nanosecond()),
3045 @"+00:00:00.000000001",
3046 );
3047
3048 insta::assert_snapshot!(
3049 p(1.second().nanoseconds(123)),
3050 @"00:00:01.000000123",
3051 );
3052 insta::assert_snapshot!(
3053 p(1.second().milliseconds(123)),
3054 @"00:00:01.123",
3055 );
3056 insta::assert_snapshot!(
3057 p(1.second().milliseconds(1_123)),
3058 @"00:00:02.123",
3059 );
3060 insta::assert_snapshot!(
3061 p(1.second().milliseconds(61_123)),
3062 @"00:00:62.123",
3063 );
3064 }
3065
3066 #[test]
3067 fn print_span_hms_fraction_fixed_precision() {
3068 let printer = || SpanPrinter::new().hours_minutes_seconds(true);
3069 let p = |precision, span| {
3070 printer().precision(Some(precision)).span_to_string(&span)
3071 };
3072
3073 insta::assert_snapshot!(p(3, 1.second()), @"00:00:01.000");
3074 insta::assert_snapshot!(
3075 p(3, 1.second().milliseconds(1)),
3076 @"00:00:01.001",
3077 );
3078 insta::assert_snapshot!(
3079 p(3, 1.second().milliseconds(123)),
3080 @"00:00:01.123",
3081 );
3082 insta::assert_snapshot!(
3083 p(3, 1.second().milliseconds(100)),
3084 @"00:00:01.100",
3085 );
3086
3087 insta::assert_snapshot!(p(0, 1.second()), @"00:00:01");
3088 insta::assert_snapshot!(p(0, 1.second().milliseconds(1)), @"00:00:01");
3089 insta::assert_snapshot!(
3090 p(1, 1.second().milliseconds(999)),
3091 @"00:00:01.9",
3092 );
3093 }
3094
3095 #[test]
3096 fn print_duration_hms() {
3097 let printer = || SpanPrinter::new().hours_minutes_seconds(true);
3098 let p = |secs| {
3099 printer().duration_to_string(&SignedDuration::from_secs(secs))
3100 };
3101
3102 insta::assert_snapshot!(p(1), @"00:00:01");
3106 insta::assert_snapshot!(p(2), @"00:00:02");
3107 insta::assert_snapshot!(p(10), @"00:00:10");
3108 insta::assert_snapshot!(p(100), @"00:01:40");
3109
3110 insta::assert_snapshot!(p(1 * 60), @"00:01:00");
3111 insta::assert_snapshot!(p(2 * 60), @"00:02:00");
3112 insta::assert_snapshot!(p(10 * 60), @"00:10:00");
3113 insta::assert_snapshot!(p(100 * 60), @"01:40:00");
3114
3115 insta::assert_snapshot!(p(1 * 60 * 60), @"01:00:00");
3116 insta::assert_snapshot!(p(2 * 60 * 60), @"02:00:00");
3117 insta::assert_snapshot!(p(10 * 60 * 60), @"10:00:00");
3118 insta::assert_snapshot!(p(100 * 60 * 60), @"100:00:00");
3119
3120 insta::assert_snapshot!(
3121 p(60 * 60 + 60 + 1),
3122 @"01:01:01",
3123 );
3124 insta::assert_snapshot!(
3125 p(2 * 60 * 60 + 2 * 60 + 2),
3126 @"02:02:02",
3127 );
3128 insta::assert_snapshot!(
3129 p(10 * 60 * 60 + 10 * 60 + 10),
3130 @"10:10:10",
3131 );
3132 insta::assert_snapshot!(
3133 p(100 * 60 * 60 + 100 * 60 + 100),
3134 @"101:41:40",
3135 );
3136 }
3137
3138 #[test]
3139 fn print_duration_hms_sign() {
3140 let printer = |direction| {
3141 SpanPrinter::new().hours_minutes_seconds(true).direction(direction)
3142 };
3143 let p = |direction, secs| {
3144 printer(direction)
3145 .duration_to_string(&SignedDuration::from_secs(secs))
3146 };
3147
3148 insta::assert_snapshot!(p(Direction::Auto, 1), @"00:00:01");
3149 insta::assert_snapshot!(p(Direction::Sign, 1), @"00:00:01");
3150 insta::assert_snapshot!(p(Direction::ForceSign, 1), @"+00:00:01");
3151 insta::assert_snapshot!(p(Direction::Suffix, 1), @"00:00:01");
3152
3153 insta::assert_snapshot!(p(Direction::Auto, -1), @"-00:00:01");
3154 insta::assert_snapshot!(p(Direction::Sign, -1), @"-00:00:01");
3155 insta::assert_snapshot!(p(Direction::ForceSign, -1), @"-00:00:01");
3156 insta::assert_snapshot!(p(Direction::Suffix, -1), @"00:00:01 ago");
3157 }
3158
3159 #[test]
3160 fn print_duration_hms_fraction_auto() {
3161 let printer = || SpanPrinter::new().hours_minutes_seconds(true);
3162 let p = |secs, nanos| {
3163 printer().duration_to_string(&SignedDuration::new(secs, nanos))
3164 };
3165
3166 insta::assert_snapshot!(p(0, 1), @"00:00:00.000000001");
3167 insta::assert_snapshot!(p(0, -1), @"-00:00:00.000000001");
3168 insta::assert_snapshot!(
3169 printer().direction(Direction::ForceSign).duration_to_string(
3170 &SignedDuration::new(0, 1),
3171 ),
3172 @"+00:00:00.000000001",
3173 );
3174
3175 insta::assert_snapshot!(
3176 p(1, 123),
3177 @"00:00:01.000000123",
3178 );
3179 insta::assert_snapshot!(
3180 p(1, 123_000_000),
3181 @"00:00:01.123",
3182 );
3183 insta::assert_snapshot!(
3184 p(1, 1_123_000_000),
3185 @"00:00:02.123",
3186 );
3187 insta::assert_snapshot!(
3188 p(61, 1_123_000_000),
3189 @"00:01:02.123",
3190 );
3191 }
3192
3193 #[test]
3194 fn print_duration_hms_fraction_fixed_precision() {
3195 let printer = || SpanPrinter::new().hours_minutes_seconds(true);
3196 let p = |precision, secs, nanos| {
3197 printer()
3198 .precision(Some(precision))
3199 .duration_to_string(&SignedDuration::new(secs, nanos))
3200 };
3201
3202 insta::assert_snapshot!(p(3, 1, 0), @"00:00:01.000");
3203 insta::assert_snapshot!(
3204 p(3, 1, 1_000_000),
3205 @"00:00:01.001",
3206 );
3207 insta::assert_snapshot!(
3208 p(3, 1, 123_000_000),
3209 @"00:00:01.123",
3210 );
3211 insta::assert_snapshot!(
3212 p(3, 1, 100_000_000),
3213 @"00:00:01.100",
3214 );
3215
3216 insta::assert_snapshot!(p(0, 1, 0), @"00:00:01");
3217 insta::assert_snapshot!(p(0, 1, 1_000_000), @"00:00:01");
3218 insta::assert_snapshot!(
3219 p(1, 1, 999_000_000),
3220 @"00:00:01.9",
3221 );
3222 }
3223}