Carbon Dating

16 May 2020

, ,


Over the years, I’ve built a whole library of helper functions for working with dates in php, but I think they’re all a thing of the past now. Because I’ve been playing with Carbon Date library by Brian Nesbitt. It’s best described as Php’s DateTime for humans.

Here follows a quick summary, but I’m just looking at the very tip of the iceberg. Lots more!

Creation

The best way to get started with Carbon is just to pass a human readable date string into its constructor, followed by an optional timezone.

Carbon inherits from the php DateTime class and will accept any date/time string which can be handled by the parent class.

use \Carbon\Carbon;
$carbon = new Carbon();  // current date & time for default timezone
$carbon = new Carbon('1 May 2020');
$carbon = new Carbon('2020-05-01');
$carbon = new Carbon('next month');
$carbon = new Carbon('first day of last month');

There are a lot of explicit static methods for Instantiation.

echo Carbon::now();  // 2020-05-16 15:05:56
echo Carbon::yesterday(); // 2020-05-15 00:00:00
echo Carbon::today();  // 2020-050-16 00:00:00

Exception will be thrown if Carbon fails to parse the string.

try {
    $carbon = new Carbon('2020-15-15');
}
catch (\Exception $e) {
    echo "That didn't work";
}

There are also a group of createXXX() helpers, where you can pass any number of variables.

echo Carbon::create(2020,1,21); // 2020-01-21 00:00:00

By default Carbon::create doesn’t throw an exception if a date overflows, and assumes that 32/Jan is the same as 1/Feb.

echo Carbon::create(2020,1,32);  // Carbon is just fine with this

But you can force error checking this with createSafe

try {
    echo Carbon::createSafe(2020,1,32);
}
catch (\Carbon\Exceptions\InvalidDateException $e) {
    echo "I didn't like that";
}

Formatting and Outputting

All of the available toXXXString() methods rely on the base class method DateTime::format().

There’s a __toString() method so a Carbon instance will be output as a pretty date time string when used in a string context.

echo (new Carbon('-3 days'))->toDateString();  // 2020-05-13
echo Carbon::parse('-3 days')->toDateTimeString(); // 2020-05-13 15:18:47

For nicely formatted output, we have:

echo Carbon::parse('-3 days')->toDayDateTimeString(); // Wed, May 13, 2020 3:19 PM
echo Carbon::parse('-3 days')->toFormattedDateString(); // May 13, 2020

Or you can simply use the ‘format’ method and pass your own formatting string

echo Carbon::parse('-3 days')->format('j M Y g:ia');  // 13 May 2020 3:22pm

And one of my favourites – get a correctly formatted date string for saving a cookie

echo Carbon::parse('+1 month')->toCookieString(); // Tuesday, 16-Jun-2020 15:23:36 BST

Difference for Humans

If you give people the date “1 May 2020”, they have to do a little mental jump to work out how long ago that was. It’s much easier to comprehend a phrase such as “2 weeks ago”

That’s what diffForHumans() does. It compares the current date and time to that stored in the Carbon date instance, and output the result as a nice human readable “XX ago” string.

echo Carbon::parse('-3 days')->diffForHumans();  // 3 days ago
echo Carbon::parse('1 May 2020')->diffForHumans();  // 2 weeks ago
echo Carbon::parse('1 May 2019')->diffForHumans();  // 1 year ago

Adding and Substracting

I always found the php DateInteval class deeply frustating. I can never remember how the interval string works. But Carbon extends this class, and provides a very nice wrapper without losing any of the basic functionality.

Instead of doing:

echo (new DateTime('2020-01-01'))
    ->add(new DateInterval('P4W'))  // add a period of 4 weeks
    ->format('Y-m-d');  // 2020-01-29

I can do this (and I am much happier about it):

echo Carbon::parse('2020-01-01')->addWeeks(4);  // 2020-01-29 00:00:00

Boolean Functions

There’s a whole buffet of helper functions, the purpose of which should really be obvious from their names.

$dt = Carbon::now();

$dt->isFuture();
$dt->isPast();
$dt->isNextYear();
$dt->isLastYear();
$dt->isNextMonth();
$dt->isCurrentMonth();
$dt->isLastMonth();

$dt->isWeekday();
$dt->isWeekend();
$dt->isMonday();  // also isTuesday(), isWednesday(), etc
$dt->isDayOfWeek(Carbon::SATURDAY); // is a saturday
$dt->isLastOfMonth(); // is the last day of the month

Testing Dates

I have a lot of code scenarios where action depends on the current date. E.g. at the end of the month, send these reports to the accounts department. Or, if it’s a weekday (except on bank holidays), staff have to input results of the safety checks.

Carbon lets me fake dates for unit tests.

$testDate = Carbon::create('first day of next month');
Carbon::setTestNow($testDate);

ReportingClass::doStuff(); 

$this->assertDatabaseHas('stuff',[
    // check for data
]);

// And tear-down for next test
Carbon::setTestNow();

Quick Summary – TL;DR

Carbon adds a lot of helpful methods to the core class, and makes working with dates easier, less frustrating, and less error prone. Also (and this is so important) excellent documentation. So rare. So many of us hate writing documentation.