Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Carbon Laravel Package

nesbot/carbon

Carbon is a popular PHP date/time library that extends DateTime with a fluent, human-readable API for parsing, formatting, comparing, and manipulating dates, plus localization, relative time, intervals, and test helpers. It is widely used in Laravel and standalone PHP projects.

View on GitHub
Deep Wiki
Context7

Getting Started

Carbon is already installed in every Laravel app (nesbot/carbon ships as a framework dependency via illuminate/support), so there is nothing to add — the work is learning which of the three classes to type-hint:

  • Carbon\Carbon — mutable, what Illuminate\Support\Carbon (and therefore now(), model datetime casts) extends.
  • Carbon\CarbonImmutable — every modifier returns a copy; use for value objects, DTOs and anything passed across layers.
  • Carbon\CarbonInterval / Carbon\CarbonPeriod — durations and date ranges, the two most under-used pieces.

First look: run composer show nesbot/carbon to see which major you are on, then read the versioned docs (carbon.nesbot.com) for that major, and check vendor/nesbot/carbon/src/Carbon/Lang/<locale>.php when a translation looks wrong — that file is the source of truth for localized output.

First use case worth doing on day one: replace string date math in one feature with Carbon.

// before
$expires = date('Y-m-d H:i:s', strtotime($created) + 7 * 86400);

// after
$expires = CarbonImmutable::parse($created)->addWeek();
$expires->isPast();                 // bool
$expires->diffForHumans();          // "in 6 days"
$expires->isoFormat('LLL');         // localized, per-user locale

Then confirm your model casts and config/app.php timezone are what you think they are — almost every Carbon bug report is really a timezone or cast bug.

Implementation Patterns

Casts are the integration point. Prefer immutable_datetime / immutable_date for new columns so $model->starts_at->addDay() can never mutate a loaded attribute (which Laravel will happily persist as dirty). Use datetime:Y-m-d only for presentation-shaped casts, and format in the resource layer instead.

Parse user input explicitly, never with parse().

$date = CarbonImmutable::createFromFormat('!d/m/Y', $request->input('date'), $request->user()->timezone);
abort_unless($date, 422); // 3.x returns null/throws InvalidFormatException depending on method

! (or trailing |) stops missing time components being filled from now — a classic source of "the date saved with the current time" bugs.

Localization is per-instance, not global. Set nothing globally; carry the user's locale:

$date->locale($user->locale)->isoFormat('LLLL');   // Tuesday, 3 September 2024 14:05
$date->locale($user->locale)->diffForHumans(['parts' => 2, 'short' => true]);
$date->locale($user->locale)->calendar();          // "Today at 14:05"

Since 3.9 Carbon also honours Laravel's app.fallback_locale for missing strings, so fr_CA partially falling back to fr→en works without you shimming translations.

Durations belong in CarbonInterval, not raw seconds.

CarbonInterval::seconds($uptime)->cascade()->forHumans(['short' => true]); // "2d 4h"
CarbonInterval::make('1 month 3 days')->totalDays;

Ranges/reports use CarbonPeriod.

foreach (CarbonImmutable::parse($from)->toPeriod($to, '1 week') as $week) { ... }
CarbonPeriod::create($start, '1 day', $end)->filter(fn ($d) => $d->isWeekday());

Business logic: use the built-ins. addWeekdays(), subWeekdays(), isWeekend(), next(Carbon::MONDAY), startOfWeek(Carbon::SUNDAY), isSameDay(), betweenIncluded(), isNowOrPast() (3.9). These beat hand-rolled %w arithmetic and respect locale's weekStartsAt.

App-wide helpers via macros, registered in a service provider:

CarbonImmutable::macro('quarter', fn () => $this->quarter);      // careful: ->quarter exists as getter
Illuminate\Support\Carbon::macro('toLocalInput', fn () => $this->format('Y-m-d\TH:i'));

Only register macros in boot() (not register()) and register the same macro on each class family you actually use (Carbon, CarbonImmutable); they do not share a macro registry.

Testing: Laravel's $this->travel(5)->days(), travelTo(), freezeTime() wrap Carbon::setTestNow(). Prefer those over calling setTestNow() directly so teardown happens for you; on 3.11.1+ withTestNow() restores the previous frozen state instead of clearing it, which matters in nested test helpers.

Gotchas and Tips

  • Carbon 3 changed diffs. diffInDays() etc. now return floats and are signed by default (negative for past dates), where v2 gave absolute ints. $days === 0 and int type hints break silently. Wrap abs(), (int), or pass the absolute flag explicitly. Also diffInDays() across a DST boundary is not a whole number — for calendar-day counts compare startOfDay() values rather than raw timestamps.
  • Month overflow. Jan 31 ->addMonth() gives March 3. Use addMonthNoOverflow(), or 3.12's overflow: mode / anchorDay when you need "same day-of-month or end of month" semantics for billing cycles.
  • ->locale() mutates in the mutable class — now()->locale('fr') changes the instance you may have just handed to a view. Either ->copy()->locale(...) or use CarbonImmutable everywhere and stop thinking about it.
  • (string) $date output is governed by Carbon::setToStringFormat() (and Laravel's Illuminate\Support\Carbon::serializeUsing() for JSON). If your API's date format suddenly changes, look in a service provider, not the model.
  • Serialization is version-sensitive. CarbonPeriod/CarbonInterval serialized payloads changed through 3.10–3.12 (v2 compatibility, inversion, recurrence limits). Don't serialize periods into queues/cache across a version bump — rehydrate from start/end/interval.
  • Debugging parse failures: catch InvalidFormatException — it carries the expected format and the offending string. Carbon::getLastErrors() returns false (not an empty array) since 3.11.1, which flips naive if (count(...)) checks.
  • Version churn is real. Localization and diff edge cases ship in patch releases almost weekly (e.g. plus()/minus() were added, removed in 3.12.1 then restored improved in 3.12.2–3.12.3). Pin with a caret range, commit composer.lock, and read the changelog before bumping if you depend on human-diff strings or locale output in tests.
  • Upgrade path: Carbon 3 needs PHP 8.1+ and a Laravel version that allows ^3.0; on older Laravel you're on the maintained 2.7x line. Check with composer why nesbot/carbon before assuming you can jump majors.
  • Extension points, in order of preference: model casts/accessors → Carbon::macro/mixin → custom Carbon subclass → editing src/Carbon/Lang/*.php only as an upstream PR. Don't patch vendor files for translation fixes; the project's release cadence (and 3.9.1's is('02-29') and is-month overflow fixes) shows those land quickly upstream.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core