Carry a numeric rounding precision out of any unit, not just seconds
This commit is contained in:
1 parent
5192615f4e
commit
95c416685a
2 files changed
+51
-14
No files matched your search
@@ -70,21 +70,35 @@ trait Rounding
|
|||||||
|
|
||||||
$precision *= $factor;
|
$precision *= $factor;
|
||||||
|
|
||||||
// A numeric precision is in seconds, so once it spans the next coarser
|
// How many of each unit make up the next one up. Only the steps with a
|
||||||
// unit it must be re-targeted there, like "week" -> "day" above.
|
// fixed size are listed: a month holds a varying number of days, so a
|
||||||
if ($normalizedUnit === 'second' && (\is_int($precision) || \is_float($precision))) {
|
// day precision is never carried into a month.
|
||||||
$secondsPerHour = static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE;
|
$unitsPerNextUnit = [
|
||||||
$secondsPerDay = static::HOURS_PER_DAY * $secondsPerHour;
|
// @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) {
|
// A numeric precision counts units of $normalizedUnit, and that unit has
|
||||||
$normalizedUnit = 'day';
|
// a range it cannot leave, so a precision spanning the next unit up has
|
||||||
$precision /= $secondsPerDay;
|
// to be re-targeted there, the same way "week" becomes "day" above.
|
||||||
} elseif ($precision >= $secondsPerHour) {
|
if (\is_int($precision) || \is_float($precision)) {
|
||||||
$normalizedUnit = 'hour';
|
while (isset($unitsPerNextUnit[$normalizedUnit])) {
|
||||||
$precision /= $secondsPerHour;
|
[$span, $nextUnit] = $unitsPerNextUnit[$normalizedUnit];
|
||||||
} elseif ($precision >= static::SECONDS_PER_MINUTE) {
|
|
||||||
$normalizedUnit = 'minute';
|
if ($precision < $span) {
|
||||||
$precision /= static::SECONDS_PER_MINUTE;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$precision /= $span;
|
||||||
|
$normalizedUnit = $nextUnit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -237,4 +237,27 @@ class RoundTest extends AbstractTestCase
|
|||||||
// Sub-minute numeric precision must keep rounding within the second field, unchanged.
|
// Sub-minute numeric precision must keep rounding within the second field, unchanged.
|
||||||
$this->assertCarbon($dt->copy()->round(30), 2026, 1, 1, 14, 21, 30, 0);
|
$this->assertCarbon($dt->copy()->round(30), 2026, 1, 1, 14, 21, 30, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testNumericPrecisionCarryFromAnyUnit(): void
|
||||||
|
{
|
||||||
|
$dt = Carbon::parse('2026-01-01 14:21:03');
|
||||||
|
|
||||||
|
// A precision counting minutes spans a whole day just as one counting
|
||||||
|
// seconds does, so it has to carry the same way.
|
||||||
|
$this->assertCarbon($dt->copy()->roundMinutes(24 * 60), 2026, 1, 2, 0, 0, 0, 0);
|
||||||
|
$this->assertTrue($dt->copy()->roundMinutes(24 * 60)->eq($dt->copy()->round('day')));
|
||||||
|
|
||||||
|
$this->assertCarbon($dt->copy()->roundHours(24), 2026, 1, 2, 0, 0, 0, 0);
|
||||||
|
$this->assertTrue($dt->copy()->roundHours(24)->eq($dt->copy()->round('day')));
|
||||||
|
|
||||||
|
$this->assertCarbon($dt->copy()->roundMinutes(60), 2026, 1, 1, 14, 0, 0, 0);
|
||||||
|
$this->assertTrue($dt->copy()->roundMinutes(60)->eq($dt->copy()->round('hour')));
|
||||||
|
|
||||||
|
$this->assertCarbon(Carbon::parse('2026-08-20')->roundMonths(12), 2027, 1, 1, 0, 0, 0, 0);
|
||||||
|
$this->assertTrue(Carbon::parse('2026-08-20')->roundMonths(12)->eq(Carbon::parse('2026-08-20')->round('year')));
|
||||||
|
|
||||||
|
// A precision that stays inside its own unit is untouched.
|
||||||
|
$this->assertCarbon($dt->copy()->roundMinutes(30), 2026, 1, 1, 14, 30, 0, 0);
|
||||||
|
$this->assertCarbon($dt->copy()->roundSeconds(30), 2026, 1, 1, 14, 21, 0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user