This project is an independently maintained fork of phoneburner/carbon-migration-rector-rules, originally released under the MIT license, by the original project authors. This fork is neither affiliated with nor endorsed by PhoneBurner.
Automated Rector rules to migrate your PHP codebase from Carbon 2 to Carbon 3, with special attention to codebases that do NOT use UTC as the default timezone.
Carbon 3 introduced several breaking changes. Some are simple renames, but others silently change behavior (like createFromTimestamp defaulting to UTC, or diffIn* returning signed floats). These rules automate as much of the migration as possible and leave TODO comments for anything that requires manual judgment.
# Install Rector if you haven't already
composer require rector/rector --dev
# Copy the rules into your project
cp -r src/Rector /path/to/your/project/utils/rector/src/Rector
cp -r src/Set /path/to/your/project/utils/rector/src/SetAdd to your composer.json:
{
"autoload-dev": {
"psr-4": {
"Carbon3Rector\\": "utils/rector/src"
}
}
}Then:
composer dump-autoloadCreate or update your rector.php:
<?php
use Carbon3Rector\Set\CarbonMigrationRules;
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withPaths([
__DIR__ . '/src',
__DIR__ . '/app',
])
->withRules(CarbonMigrationRules::ALL);Run:
# Preview changes
vendor/bin/rector process --dry-run
# Apply changes
vendor/bin/rector process| Severity | Critical (silent behavior change) |
|---|
Carbon 3 changed createFromTimestamp() default timezone from date_default_timezone_get() to "UTC". For non-UTC codebases, this silently changes every timestamp conversion.
-$date = Carbon::createFromTimestamp($timestamp);
+$date = Carbon::createFromTimestamp($timestamp, date_default_timezone_get());| Severity | Critical (silent behavior change) |
|---|
Same as above but for createFromTimestampMs().
-$date = Carbon::createFromTimestampMs($ms);
+$date = Carbon::createFromTimestampMs($ms, date_default_timezone_get());| Severity | Critical (silent behavior change) |
|---|
diffIn* methods changed from returning positive int to returning signed float. This rule wraps calls in (int) abs(...) to preserve Carbon 2 behavior.
-$seconds = $a->diffInSeconds($b);
+$seconds = (int) abs($a->diffInSeconds($b));
// If already using absolute=true, just casts to int:
-$hours = $a->diffInHours($b, true);
+$hours = (int) $a->diffInHours($b, true);Covered methods: diffInYears, diffInMonths, diffInWeeks, diffInDays, diffInDaysFiltered, diffInHours, diffInMinutes, diffInSeconds, diffInMicroseconds, diffInMilliseconds, diffInWeekdays, diffInWeekendDays, diffInRealHours, diffInRealMinutes, diffInRealSeconds, diffInRealMicroseconds, diffInRealMilliseconds
| Severity | Medium (will cause type errors) |
|---|
create(), createFromFormat(), createFromIsoFormat(), createFromLocaleFormat(), and createFromLocaleIsoFormat() now return null instead of false on failure.
-if (Carbon::createFromFormat('Y-m-d', $input) === false) {
+if (Carbon::createFromFormat('Y-m-d', $input) === null) {| Severity | Medium (will throw exception) |
|---|
isSame*() methods no longer accept zero arguments. The Carbon 2 behavior (compare to "now") is now isCurrent*().
-$date->isSameDay();
+$date->isCurrentDay();
-$date->isSameMonth();
+$date->isCurrentMonth();| Severity | Medium (method removed) |
|---|
minValue() and maxValue() were removed. Replaced by CarbonImmutable-only methods.
-$min = Carbon::minValue();
+$min = CarbonImmutable::startOfTime();
-$max = Carbon::maxValue();
+$max = CarbonImmutable::endOfTime();| Severity | Medium (will cause error) |
|---|
All named argument tz: parameters were renamed to timezone:.
-Carbon::create(2024, 1, 1, tz: 'UTC');
+Carbon::create(2024, 1, 1, timezone: 'UTC');| Severity | Medium (method removed) |
|---|
formatLocalized() (which relied on OS strftime()) was removed. Replaced by isoFormat() with automatic strftime→isoFormat token conversion.
-$date->formatLocalized('%A %d %B %Y');
+$date->isoFormat('dddd DD MMMM YYYY');Adds TODO comments when format strings contain tokens that can't be automatically converted or when the format is dynamic.
| Severity | Low (method removed) |
|---|
Removes setUtf8() calls which no longer exist.
-Carbon::setUtf8(true);
$date = Carbon::now();| Severity | Medium (method removed, needs manual migration) |
|---|
Removes setWeekStartsAt() / setWeekEndsAt() and adds TODO comments with migration guidance.
-Carbon::setWeekStartsAt(Carbon::MONDAY);
+// TODO: [Carbon 3 migration] setWeekStartsAt() was removed in Carbon 3.
+// Pass the day explicitly to startOfWeek(\Carbon\WeekDay::Monday) or use locale-based week start.After applying the automated rules, review the MIGRATION_CHECKLIST.md file included in this package for things that need manual inspection.
Search your codebase for TODO: [Carbon 3 migration] to find all spots that need manual review.
You can run specific rules instead of the full set:
use WickedByte\CarbonMigrationRectorRules\CarbonDiffInMethodsToExplicitCastRector;
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withRules([
CarbonDiffInMethodsToExplicitCastRector::class,
]);After running Rector, use these regex patterns to find any remaining issues:
# Find diffIn* calls that might not have been caught
grep -rn 'diffIn\(Seconds\|Minutes\|Hours\|Days\|Weeks\|Months\|Years\)' src/
# Find createFromTimestamp without explicit timezone
grep -rn 'createFromTimestamp(' src/ | grep -v 'date_default_timezone_get'
# Find any remaining false comparisons with Carbon create methods
grep -rn 'createFromFormat.*=== false\|=== false.*createFromFormat' src/
# Find any remaining formatLocalized calls
grep -rn 'formatLocalized' src/
# Find comparison methods that might receive null/bool
grep -rn '->eq(\|->gt(\|->lt(\|->gte(\|->lte(\|->ne(' src/MIT