I’ve been starting the process of testing our application at work on the latest version of the Laravel framework. Version 11 came out last month. I used Laravel Shift to migrate the code base from 10.x to 11.x, which saved a lot of time.
When it was done, however, I had several tests that weren’t passing related to date calculations in our code. Errors like this one:
1) Tests\Unit\Helpers\DateHelper::test_days_remaining
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2'
+'-2'
The Cause
Carbon 3.x includes a changes made in the ♻️ Cleanup diffIn* diffInReal* floatDiffIn* floatDiffInReal* pull request that convert the return value to a float. This has something to do with a bug with DST calculation. This also seems to have resulted in positive return values becoming negative values.
This is a known breaking change in the Carbon library. It just tripped me up since in my case Carbon was updated by upgrading Laravel. I wasn’t specifically thinking of “What changed in Carbon?”
Affected Methods
The following methods in Carbon are affected:
- diffInDays
- diffInDaysFiltered
- diffInHours
- diffInHoursFiltered
- diffInMicroseconds
- diffInMilliseconds
- diffInMinutes
- diffInMonths
- diffInQuarters
- diffInSeconds
- diffInUnit
- diffInWeekdays
- diffInWeekendDays
- diffInWeeks
- diffInYears
Finding these functions in your code
Thank you to the author of the PR, kylekatarnls, for providing the RegEx to search for in your code.
If your IDE supports searching by RegEx, use this to find all instances of the affected functions:
->diffIn[a-zA-Z]+\(
Solution #1
You can roll back to Carbon 2.0 as long as you don’t have any other packages that require Carbon 3. The Laravel Framework will still work on Carbon 2.x:
"nesbot/carbon": "^2.72",
In order to use Carbon 2.x in your application, add this to your composer.json
file and run composer update
to downgrade to 2.x
Solution #2
Each of the affected functions accepts a second parameter, $absolute
, that is defaulted to false. You also need to cast the float to an int with (int)
. Doing both of these things, you’ll get the same result you did in Carbon 2.7.x.
Example:
$startDate->diffInDays(Carbon::now()); //Becomes: (int) $startDate->diffInDays(Carbon::now(), true);
Solution #3
I’ll quote the author of the PR:
It might also be a good occasion to review those calls and wonder “what if date in parameter is before the date on which the method is called?” and re-assert your code is correctly handling the case.
For instance if you have
kylekatarnis$value = $date->diffInHours($other)
and$other
is always before$date
, then the relevant thing to do instead of using theabsolute: true
flag is rather to just take the opposite value:$value = - $date->diffInHours($other)
or to flip them if$other
is also aCarbon
object:$value = $other->diffInHours($date)
it will clear ambiguity.
Conclusion
The biggest takeaway from this exercise was the value in having a solid test coverage that caught this breaking change. Write your tests, folks!