From d58d22104b8cc0dc435caaf8da003aa4ee28feed Mon Sep 17 00:00:00 2001 From: kylekatarnls Date: Tue, 19 Mar 2024 15:49:00 +0100 Subject: [PATCH] Deprecate Real diff in favor of UTC diff --- src/Carbon/CarbonInterface.php | 18 +++++++---- src/Carbon/CarbonInterval.php | 2 +- src/Carbon/Traits/Date.php | 25 +++++++++++++-- src/Carbon/Traits/Difference.php | 54 ++++++++++++++++++++++---------- 4 files changed, 73 insertions(+), 26 deletions(-) diff --git a/src/Carbon/CarbonInterface.php b/src/Carbon/CarbonInterface.php index d44b7c4574..f24311c463 100644 --- a/src/Carbon/CarbonInterface.php +++ b/src/Carbon/CarbonInterface.php @@ -1626,10 +1626,11 @@ public function diffForHumans($other = null, $syntax = null, $short = false, $pa * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * * @return float */ - public function diffInDays($date = null, bool $absolute = false): float; + public function diffInDays($date = null, bool $absolute = false, bool $utc = false): float; /** * Get the difference in days using a filter closure rounded down. @@ -1698,20 +1699,22 @@ public function diffInMinutes($date = null, bool $absolute = false): float; * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * * @return float */ - public function diffInMonths($date = null, bool $absolute = false): float; + public function diffInMonths($date = null, bool $absolute = false, bool $utc = false): float; /** * Get the difference in quarters rounded down. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * * @return float */ - public function diffInQuarters($date = null, bool $absolute = false): float; + public function diffInQuarters($date = null, bool $absolute = false, bool $utc = false): float; /** * Get the difference in seconds rounded down. @@ -1729,10 +1732,11 @@ public function diffInSeconds($date = null, bool $absolute = false): float; * century, millennium * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * * @return float */ - public function diffInUnit(Unit|string $unit, $date = null, bool $absolute = false): float; + public function diffInUnit(Unit|string $unit, $date = null, bool $absolute = false, bool $utc = false): float; /** * Get the difference in weekdays rounded down. @@ -1759,20 +1763,22 @@ public function diffInWeekendDays($date = null, bool $absolute = false): int; * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * * @return float */ - public function diffInWeeks($date = null, bool $absolute = false): float; + public function diffInWeeks($date = null, bool $absolute = false, bool $utc = false): float; /** * Get the difference in years * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * * @return float */ - public function diffInYears($date = null, bool $absolute = false): float; + public function diffInYears($date = null, bool $absolute = false, bool $utc = false): float; /** * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. diff --git a/src/Carbon/CarbonInterval.php b/src/Carbon/CarbonInterval.php index ef01f88afe..aab960eb3c 100644 --- a/src/Carbon/CarbonInterval.php +++ b/src/Carbon/CarbonInterval.php @@ -3196,7 +3196,7 @@ private function checkIntegerValue(string $name, mixed $value): void "From 3.0.0, decimal part will no longer be truncated and will be cascaded to smaller units.\n". "- To maintain the current behavior, use explicit cast: $name((int) \$value)\n". "- To adopt the new behavior globally, call CarbonInterval::enableFloatSetters()\n", - \E_USER_DEPRECATED + \E_USER_DEPRECATED, ); } } diff --git a/src/Carbon/Traits/Date.php b/src/Carbon/Traits/Date.php index 77feb51ac8..5837f6ec73 100644 --- a/src/Carbon/Traits/Date.php +++ b/src/Carbon/Traits/Date.php @@ -2592,8 +2592,29 @@ public static function sleep(int|float $seconds): void */ public function __call(string $method, array $parameters): mixed { - if (preg_match('/^(?:diff|floatDiff)In(?:Real)?(.+)$/', $method, $match)) { - $method = 'diffIn'.$match[1]; + if (preg_match('/^(diff|floatDiff)In(Real|UTC|Utc)?(.+)$/', $method, $match)) { + $mode = strtoupper($match[2] ?? ''); + $betterMethod = $match[1] === 'floatDiff' ? str_replace('floatDiff', 'diff', $method) : null; + + if ($mode === 'REAL') { + $mode = 'UTC'; + $betterMethod = str_replace($match[1], 'UTC', $betterMethod ?? $method); + } + + if ($betterMethod) { + @trigger_error( + "Use the method $betterMethod instead to make it more explicit about what it does.\n". + 'On next major version, "float" prefix will be removed (as all diff are now returning floating numbers)'. + ' and "Real" methods will be removed in favor of "UTC" because what it actually does is to convert both'. + ' dates to UTC timezone before comparison, while by default it does it only if both dates don\'t have'. + ' exactly the same timezone (Note: 2 timezones with the same offset but different names are considered'. + ' different as it\'s not safe to assume they will always have the same offset).', + \E_USER_DEPRECATED, + ); + } + + $method = 'diffIn'.$match[3]; + $parameters['utc'] = ($mode === 'UTC'); if (method_exists($this, $method)) { return $this->$method(...$parameters); diff --git a/src/Carbon/Traits/Difference.php b/src/Carbon/Traits/Difference.php index d29c81da4d..14e8aeaa79 100644 --- a/src/Carbon/Traits/Difference.php +++ b/src/Carbon/Traits/Difference.php @@ -99,10 +99,11 @@ public function diff($date = null, bool $absolute = false, array $skip = []): Ca * century, millennium * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * * @return float */ - public function diffInUnit(Unit|string $unit, $date = null, bool $absolute = false): float + public function diffInUnit(Unit|string $unit, $date = null, bool $absolute = false, bool $utc = false): float { $unit = static::pluralUnit($unit instanceof Unit ? $unit->value : rtrim($unit, 'z')); $method = 'diffIn'.$unit; @@ -111,7 +112,7 @@ public function diffInUnit(Unit|string $unit, $date = null, bool $absolute = fal throw new UnknownUnitException($unit); } - return $this->$method($date, $absolute); + return $this->$method($date, $absolute, $utc); } /** @@ -119,18 +120,27 @@ public function diffInUnit(Unit|string $unit, $date = null, bool $absolute = fal * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * * @return float */ - public function diffInYears($date = null, bool $absolute = false): float + public function diffInYears($date = null, bool $absolute = false, bool $utc = false): float { $start = $this; $end = $this->resolveCarbon($date); + + if ($utc) { + $start = $start->avoidMutation()->utc(); + $end = $end->avoidMutation()->utc(); + } + $ascending = ($start <= $end); $sign = $absolute || $ascending ? 1 : -1; + if (!$ascending) { [$start, $end] = [$end, $start]; } + $yearsDiff = (int) $start->diff($end, $absolute)->format('%r%y'); /** @var Carbon|CarbonImmutable $floorEnd */ $floorEnd = $start->copy()->addYears($yearsDiff); @@ -154,12 +164,13 @@ public function diffInYears($date = null, bool $absolute = false): float * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * * @return float */ - public function diffInQuarters($date = null, bool $absolute = false): float + public function diffInQuarters($date = null, bool $absolute = false, bool $utc = false): float { - return $this->diffInMonths($date, $absolute) / static::MONTHS_PER_QUARTER; + return $this->diffInMonths($date, $absolute, $utc) / static::MONTHS_PER_QUARTER; } /** @@ -167,13 +178,20 @@ public function diffInQuarters($date = null, bool $absolute = false): float * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * * @return float */ - public function diffInMonths($date = null, bool $absolute = false): float + public function diffInMonths($date = null, bool $absolute = false, bool $utc = false): float { $start = $this; - $end = $this->resolveCarbon($date)->avoidMutation()->setTimezone($this->tz); + $end = $this->resolveCarbon($date); + $compareUsingUtc = $utc || ($end->timezoneName !== $start->timezoneName); + + if ($compareUsingUtc) { + $start = $start->avoidMutation()->utc(); + $end = $end->avoidMutation()->utc(); + } [$yearStart, $monthStart, $dayStart] = explode('-', $start->format('Y-m-dHisu')); [$yearEnd, $monthEnd, $dayEnd] = explode('-', $end->format('Y-m-dHisu')); @@ -196,14 +214,14 @@ public function diffInMonths($date = null, bool $absolute = false): float } /** @var Carbon|CarbonImmutable $floorEnd */ - $floorEnd = $start->copy()->addMonths($monthsDiff); + $floorEnd = $start->avoidMutation()->addMonths($monthsDiff); if ($floorEnd >= $end) { return $sign * $monthsDiff; } /** @var Carbon|CarbonImmutable $startOfMonthAfterFloorEnd */ - $startOfMonthAfterFloorEnd = $floorEnd->copy()->addMonthNoOverflow()->startOfMonth(); + $startOfMonthAfterFloorEnd = $floorEnd->avoidMutation()->addMonthNoOverflow()->startOfMonth(); if ($startOfMonthAfterFloorEnd > $end) { return $sign * ($monthsDiff + $floorEnd->diffInDays($end) / $floorEnd->daysInMonth); @@ -217,12 +235,13 @@ public function diffInMonths($date = null, bool $absolute = false): float * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * * @return float */ - public function diffInWeeks($date = null, bool $absolute = false): float + public function diffInWeeks($date = null, bool $absolute = false, bool $utc = false): float { - return $this->diffInDays($date, $absolute) / static::DAYS_PER_WEEK; + return $this->diffInDays($date, $absolute, $utc) / static::DAYS_PER_WEEK; } /** @@ -230,23 +249,24 @@ public function diffInWeeks($date = null, bool $absolute = false): float * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * * @return float */ - public function diffInDays($date = null, bool $absolute = false): float + public function diffInDays($date = null, bool $absolute = false, bool $utc = false): float { $date = $this->resolveCarbon($date); $current = $this; - $sameTimezone = ($date->timezoneName === $current->timezoneName); + $compareUsingUtc = $utc || ($date->timezoneName !== $current->timezoneName); - if (!$sameTimezone) { - $date = $date->copy()->utc(); - $current = $current->copy()->utc(); + if ($compareUsingUtc) { + $date = $date->avoidMutation()->utc(); + $current = $current->avoidMutation()->utc(); } $interval = $current->diffAsDateInterval($date, $absolute); - if ($sameTimezone) { + if (!$compareUsingUtc) { $minutes = $interval->i + ($interval->s + $interval->f) / static::SECONDS_PER_MINUTE; $hours = $interval->h + $minutes / static::MINUTES_PER_HOUR;