Carry a numeric rounding precision out of any unit, not just seconds

This commit is contained in:
dualfroz committed 2026-09-23 07:05:08 +02:00
1 parent 5192615f4e
commit 95c416685a
2 files changed
+51 -14

No files matched your search

+28 -14
View File
@@ -70,21 +70,35 @@ trait Rounding
$precision *= $factor;
// A numeric precision is in seconds, so once it spans the next coarser
// unit it must be re-targeted there, like "week" -> "day" above.
if ($normalizedUnit === 'second' && (\is_int($precision) || \is_float($precision))) {
$secondsPerHour = static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE;
$secondsPerDay = static::HOURS_PER_DAY * $secondsPerHour;
// How many of each unit make up the next one up. Only the steps with a
// fixed size are listed: a month holds a varying number of days, so a
// day precision is never carried into a month.
$unitsPerNextUnit = [
// @call roundUnit
'microsecond' => [static::MICROSECONDS_PER_SECOND, 'second'],
// @call roundUnit
'second' => [static::SECONDS_PER_MINUTE, 'minute'],
// @call roundUnit
'minute' => [static::MINUTES_PER_HOUR, 'hour'],
// @call roundUnit
'hour' => [static::HOURS_PER_DAY, 'day'],
// @call roundUnit
'month' => [static::MONTHS_PER_YEAR, 'year'],
];
if ($precision >= $secondsPerDay) {
$normalizedUnit = 'day';
$precision /= $secondsPerDay;
} elseif ($precision >= $secondsPerHour) {
$normalizedUnit = 'hour';
$precision /= $secondsPerHour;
} elseif ($precision >= static::SECONDS_PER_MINUTE) {
$normalizedUnit = 'minute';
$precision /= static::SECONDS_PER_MINUTE;
// A numeric precision counts units of $normalizedUnit, and that unit has
// a range it cannot leave, so a precision spanning the next unit up has
// to be re-targeted there, the same way "week" becomes "day" above.
if (\is_int($precision) || \is_float($precision)) {
while (isset($unitsPerNextUnit[$normalizedUnit])) {
[$span, $nextUnit] = $unitsPerNextUnit[$normalizedUnit];
if ($precision < $span) {
break;
}
$precision /= $span;
$normalizedUnit = $nextUnit;
}
}