Boundaries.php
4 years ago
Cast.php
4 years ago
Comparison.php
2 years ago
Converter.php
2 years ago
Creator.php
2 years ago
Date.php
9 months ago
DeprecatedProperties.php
4 years ago
Difference.php
2 years ago
IntervalRounding.php
2 years ago
IntervalStep.php
4 years ago
Localization.php
9 months ago
Macro.php
4 years ago
MagicParameter.php
2 years ago
Mixin.php
2 years ago
Modifiers.php
4 years ago
Mutability.php
4 years ago
ObjectInitialisation.php
4 years ago
Options.php
2 years ago
Rounding.php
2 years ago
Serialization.php
2 years ago
Test.php
9 months ago
Timestamp.php
1 year ago
ToStringFormat.php
3 years ago
Units.php
2 years ago
Week.php
4 years ago
index.php
3 years ago
Rounding.php
126 lines
| 1 | <?php |
| 2 | namespace MailPoetVendor\Carbon\Traits; |
| 3 | if (!defined('ABSPATH')) exit; |
| 4 | use MailPoetVendor\Carbon\CarbonInterface; |
| 5 | use MailPoetVendor\Carbon\Exceptions\UnknownUnitException; |
| 6 | trait Rounding |
| 7 | { |
| 8 | use IntervalRounding; |
| 9 | public function roundUnit($unit, $precision = 1, $function = 'round') |
| 10 | { |
| 11 | $metaUnits = [ |
| 12 | // @call roundUnit |
| 13 | 'millennium' => [static::YEARS_PER_MILLENNIUM, 'year'], |
| 14 | // @call roundUnit |
| 15 | 'century' => [static::YEARS_PER_CENTURY, 'year'], |
| 16 | // @call roundUnit |
| 17 | 'decade' => [static::YEARS_PER_DECADE, 'year'], |
| 18 | // @call roundUnit |
| 19 | 'quarter' => [static::MONTHS_PER_QUARTER, 'month'], |
| 20 | // @call roundUnit |
| 21 | 'millisecond' => [1000, 'microsecond'], |
| 22 | ]; |
| 23 | $normalizedUnit = static::singularUnit($unit); |
| 24 | $ranges = \array_merge(static::getRangesByUnit($this->daysInMonth), [ |
| 25 | // @call roundUnit |
| 26 | 'microsecond' => [0, 999999], |
| 27 | ]); |
| 28 | $factor = 1; |
| 29 | if ($normalizedUnit === 'week') { |
| 30 | $normalizedUnit = 'day'; |
| 31 | $precision *= static::DAYS_PER_WEEK; |
| 32 | } |
| 33 | if (isset($metaUnits[$normalizedUnit])) { |
| 34 | [$factor, $normalizedUnit] = $metaUnits[$normalizedUnit]; |
| 35 | } |
| 36 | $precision *= $factor; |
| 37 | if (!isset($ranges[$normalizedUnit])) { |
| 38 | throw new UnknownUnitException($unit); |
| 39 | } |
| 40 | $found = \false; |
| 41 | $fraction = 0; |
| 42 | $arguments = null; |
| 43 | $initialValue = null; |
| 44 | $factor = $this->year < 0 ? -1 : 1; |
| 45 | $changes = []; |
| 46 | $minimumInc = null; |
| 47 | foreach ($ranges as $unit => [$minimum, $maximum]) { |
| 48 | if ($normalizedUnit === $unit) { |
| 49 | $arguments = [$this->{$unit}, $minimum]; |
| 50 | $initialValue = $this->{$unit}; |
| 51 | $fraction = $precision - \floor($precision); |
| 52 | $found = \true; |
| 53 | continue; |
| 54 | } |
| 55 | if ($found) { |
| 56 | $delta = $maximum + 1 - $minimum; |
| 57 | $factor /= $delta; |
| 58 | $fraction *= $delta; |
| 59 | $inc = ($this->{$unit} - $minimum) * $factor; |
| 60 | if ($inc !== 0.0) { |
| 61 | $minimumInc = $minimumInc ?? $arguments[0] / \pow(2, 52); |
| 62 | // If value is still the same when adding a non-zero increment/decrement, |
| 63 | // it means precision got lost in the addition |
| 64 | if (\abs($inc) < $minimumInc) { |
| 65 | $inc = $minimumInc * ($inc < 0 ? -1 : 1); |
| 66 | } |
| 67 | // If greater than $precision, assume precision loss caused an overflow |
| 68 | if ($function !== 'floor' || \abs($arguments[0] + $inc - $initialValue) >= $precision) { |
| 69 | $arguments[0] += $inc; |
| 70 | } |
| 71 | } |
| 72 | $changes[$unit] = \round($minimum + ($fraction ? $fraction * $function(($this->{$unit} - $minimum) / $fraction) : 0)); |
| 73 | // Cannot use modulo as it lose double precision |
| 74 | while ($changes[$unit] >= $delta) { |
| 75 | $changes[$unit] -= $delta; |
| 76 | } |
| 77 | $fraction -= \floor($fraction); |
| 78 | } |
| 79 | } |
| 80 | [$value, $minimum] = $arguments; |
| 81 | $normalizedValue = \floor($function(($value - $minimum) / $precision) * $precision + $minimum); |
| 82 | $result = $this; |
| 83 | foreach ($changes as $unit => $value) { |
| 84 | $result = $result->{$unit}($value); |
| 85 | } |
| 86 | return $result->{$normalizedUnit}($normalizedValue); |
| 87 | } |
| 88 | public function floorUnit($unit, $precision = 1) |
| 89 | { |
| 90 | return $this->roundUnit($unit, $precision, 'floor'); |
| 91 | } |
| 92 | public function ceilUnit($unit, $precision = 1) |
| 93 | { |
| 94 | return $this->roundUnit($unit, $precision, 'ceil'); |
| 95 | } |
| 96 | public function round($precision = 1, $function = 'round') |
| 97 | { |
| 98 | return $this->roundWith($precision, $function); |
| 99 | } |
| 100 | public function floor($precision = 1) |
| 101 | { |
| 102 | return $this->round($precision, 'floor'); |
| 103 | } |
| 104 | public function ceil($precision = 1) |
| 105 | { |
| 106 | return $this->round($precision, 'ceil'); |
| 107 | } |
| 108 | public function roundWeek($weekStartsAt = null) |
| 109 | { |
| 110 | return $this->closest($this->avoidMutation()->floorWeek($weekStartsAt), $this->avoidMutation()->ceilWeek($weekStartsAt)); |
| 111 | } |
| 112 | public function floorWeek($weekStartsAt = null) |
| 113 | { |
| 114 | return $this->startOfWeek($weekStartsAt); |
| 115 | } |
| 116 | public function ceilWeek($weekStartsAt = null) |
| 117 | { |
| 118 | if ($this->isMutable()) { |
| 119 | $startOfWeek = $this->avoidMutation()->startOfWeek($weekStartsAt); |
| 120 | return $startOfWeek != $this ? $this->startOfWeek($weekStartsAt)->addWeek() : $this; |
| 121 | } |
| 122 | $startOfWeek = $this->startOfWeek($weekStartsAt); |
| 123 | return $startOfWeek != $this ? $startOfWeek->addWeek() : $this->avoidMutation(); |
| 124 | } |
| 125 | } |
| 126 |