blog.thms.uk

Creating a Custom PHP Carbon formatter using macros

Carbon is an absolutely amazing extension for handling date and time for PHP users. Something I wasn’t aware of until recently, is that Carbon has support for Macros which allows us to extend its functionality.

In my case I needed a formatter for date intervals that rounds the display. This is best explained in an example. I have two Carbon instances that are almost but not quite 2 days apart from one another, and want to get the longAbsoluteDiffForHumans() between the two:

use Carbon\Carbon;

$date1 = Carbon::parse('2025-01-08 08:00:00');
$date2 = Carbon::parse('2025-01-10 07:59:59');

$date2->longAbsoluteDiffForHumans($date1); // "1 day"

Annoyingly this rounds down to ‘1 day’ despite the difference really being for all intents and purposes 2 days.

Now, you can change the rounding mechanism in Carbon by simply using the underlying diffForHumans() method with some extra arguments, and that’s great:


use Carbon\Carbon;
use Carbon\CarbonInterface;

$date1 = Carbon::parse('2025-01-08 08:00:00');
$date2 = Carbon::parse('2025-01-10 07:59:59');

$date2->diffForHumans(
  $date1,
  CarbonInterface::DIFF_ABSOLUTE,
  options: CarbonInterface::ROUND
); // "2 days"

But it’s also quite verbose and hard to remember if you need to use it more than once. And that’s where macros come in really handy: They allow us to extend Carbon’s API with additional methods, by simply calling the Carbon::macro() method with the name of our macro as the first argument and a closure as the second argument. This will make the closure action available on all Carbon instances.

I’m using Laravel in this project, so the best place to register my macro is in the Service Provider’s boot method. I’ll also add the $other and $parts parameters, so that the signature is compatible with that of longAbsoluteDiffForHumans():

namespace App\Providers;

use Carbon\Carbon;
use Carbon\CarbonInterface;
use DateTimeInterface;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Carbon::macro(
            'longRoundedAbsoluteDiffForHumans',
            fn(DateTimeInterface $other = null, int $parts = 1): string => Carbon::diffForHumans(
                $other,
                CarbonInterface::DIFF_ABSOLUTE,
                parts: $parts,
                options: CarbonInterface::ROUND,
            ),
        );
    }
}

Now I can use this anywhere within my project as follows:

use Carbon\Carbon;

$date1 = Carbon::parse('2025-01-08 08:00:00');
$date2 = Carbon::parse('2025-01-10 07:59:59');

$date2->longRoundedAbsoluteDiffForHumans($date1); // "2 days"

However, whilst less verbose (and arguably easier to understand) than the first attempt, it’s still easy to forget and easier to mis-type. So we want what every developer craves: Auto-completion! Thankfully we have two options:

We can use either carbon-cli, or (if you use Laravel with PhpStorm) Laravel Idea to generate helper code for the IDE to consume. I already use the latter in my project, so a simple CMD+SHIFT+. later, I got auto-completion in my IDE - no further configuration needed.

And that’s how I’ve added a longRoundedAbsoluteDiffForHumans() method to my Carbon instances in my Laravel project.