<?php namespace MailPoetVendor\Carbon\Traits; if (!defined('ABSPATH')) exit; use MailPoetVendor\Carbon\CarbonInterface; use MailPoetVendor\Carbon\Exceptions\UnknownUnitException; trait Rounding { use IntervalRounding; public function roundUnit($unit, $precision = 1, $function = 'round') { $metaUnits = [ // @call roundUnit 'millennium' => [static::YEARS_PER_MILLENNIUM, 'year'], // @call roundUnit 'century' => [static::YEARS_PER_CENTURY, 'year'], // @call roundUnit 'decade' => [static::YEARS_PER_DECADE, 'year'], // @call roundUnit 'quarter' => [static::MONTHS_PER_QUARTER, 'month'], // @call roundUnit 'millisecond' => [1000, 'microsecond'], ]; $normalizedUnit = static::singularUnit($unit); $ranges = \array_merge(static::getRangesByUnit(), [ // @call roundUnit 'microsecond' => [0, 999999], ]); $factor = 1; $initialMonth = $this->month; if ($normalizedUnit === 'week') { $normalizedUnit = 'day'; $precision *= static::DAYS_PER_WEEK; } if (isset($metaUnits[$normalizedUnit])) { [$factor, $normalizedUnit] = $metaUnits[$normalizedUnit]; } $precision *= $factor; if (!isset($ranges[$normalizedUnit])) { throw new UnknownUnitException($unit); } $found = \false; $fraction = 0; $arguments = null; $factor = $this->year < 0 ? -1 : 1; $changes = []; foreach ($ranges as $unit => [$minimum, $maximum]) { if ($normalizedUnit === $unit) { $arguments = [$this->{$unit}, $minimum]; $fraction = $precision - \floor($precision); $found = \true; continue; } if ($found) { $delta = $maximum + 1 - $minimum; $factor /= $delta; $fraction *= $delta; $arguments[0] += $this->{$unit} * $factor; $changes[$unit] = \round($minimum + ($fraction ? $fraction * $function(($this->{$unit} - $minimum) / $fraction) : 0)); // Cannot use modulo as it lose double precision while ($changes[$unit] >= $delta) { $changes[$unit] -= $delta; } $fraction -= \floor($fraction); } } [$value, $minimum] = $arguments; $normalizedValue = \floor($function(($value - $minimum) / $precision) * $precision + $minimum); $result = $this->{$normalizedUnit}($normalizedValue); foreach ($changes as $unit => $value) { $result = $result->{$unit}($value); } return $normalizedUnit === 'month' && $precision <= 1 && \abs($result->month - $initialMonth) === 2 ? $result->{$normalizedUnit}($normalizedValue) : $result; } public function floorUnit($unit, $precision = 1) { return $this->roundUnit($unit, $precision, 'floor'); } public function ceilUnit($unit, $precision = 1) { return $this->roundUnit($unit, $precision, 'ceil'); } public function round($precision = 1, $function = 'round') { return $this->roundWith($precision, $function); } public function floor($precision = 1) { return $this->round($precision, 'floor'); } public function ceil($precision = 1) { return $this->round($precision, 'ceil'); } public function roundWeek($weekStartsAt = null) { return $this->closest($this->avoidMutation()->floorWeek($weekStartsAt), $this->avoidMutation()->ceilWeek($weekStartsAt)); } public function floorWeek($weekStartsAt = null) { return $this->startOfWeek($weekStartsAt); } public function ceilWeek($weekStartsAt = null) { if ($this->isMutable()) { $startOfWeek = $this->avoidMutation()->startOfWeek($weekStartsAt); return $startOfWeek != $this ? $this->startOfWeek($weekStartsAt)->addWeek() : $this; } $startOfWeek = $this->startOfWeek($weekStartsAt); return $startOfWeek != $this ? $startOfWeek->addWeek() : $this->avoidMutation(); } }