Introduction

The Carbon class is inherited from the PHP DateTime class.

<?php
namespace Carbon;

class Carbon extends \DateTime
{
    // code here
}

Carbon has all of the functions inherited from the base DateTime class. This approach allows you to access the base functionality if you see anything missing in Carbon but is there in DateTime.

You can see from the code snippet above that the Carbon class is declared in the Carbon namespace. You need to import the namespace to use Carbon without having to provide its fully qualified name each time.

use Carbon\Carbon;

Note: I live in Ottawa, Ontario, Canada and if the timezone is not specified in the examples then the default of 'America/Toronto' is to be assumed. Typically Ottawa is -0500 but when daylight savings time is on we are -0400.

Special care has been taken to ensure timezones are handled correctly, and where appropriate are based on the underlying DateTime implementation. For example all comparisons are done in UTC or in the timezone of the datetime being used.

$dtToronto = Carbon::create(2012, 1, 1, 0, 0, 0, 'America/Toronto');
$dtVancouver = Carbon::create(2012, 1, 1, 0, 0, 0, 'America/Vancouver');

echo $dtVancouver->diffInHours($dtToronto); // 3

Also is comparisons are done in the timezone of the provided Carbon instance. For example my current timezone is -13 hours from Tokyo. So Carbon::now('Asia/Tokyo')->isToday() would only return false for any time past 1 PM my time. This doesn't make sense since now() in tokyo is always today in Tokyo. Thus the comparison to now() is done in the same timezone as the current instance.

Instantiation

There are several different methods available to create a new instance of Carbon. First there is a constructor. It overrides the parent constructor and you are best to read about the first parameter from the PHP manual and understand the date/time string formats it accepts. You'll hopefully find yourself rarely using the constructor but rather relying on the explicit static methods for improved readability.

$carbon = new Carbon();                  // equivalent to Carbon::now()
$carbon = new Carbon('first day of January 2008', 'America/Vancouver');
echo get_class($carbon);                 // 'Carbon\Carbon'
$carbon = Carbon::now(-5);

You'll notice above that the timezone (2nd) parameter was passed as a string and an integer rather than a \DateTimeZone instance. All DateTimeZone parameters have been augmented so you can pass a DateTimeZone instance, string or integer offset to GMT and the timezone will be created for you. This is again shown in the next example which also introduces the now() function.

$now = Carbon::now();

$nowInLondonTz = Carbon::now(new DateTimeZone('Europe/London'));

// or just pass the timezone as a string
$nowInLondonTz = Carbon::now('Europe/London');

// or to create a date with a timezone of +1 to GMT during DST then just pass an integer
echo Carbon::now(1)->tzName;             // Europe/London

If you really love your fluid method calls and get frustrated by the extra line or ugly pair of brackets necessary when using the constructor you'll enjoy the parse method.

echo (new Carbon('first day of December 2008'))->addWeeks(2);     // 2008-12-15 00:00:00
echo Carbon::parse('first day of December 2008')->addWeeks(2);    // 2008-12-15 00:00:00

Note: before PHP 5.4 (new MyClass())->method() trigger a syntax error, if you use PHP 5.3, you need to create a variable then call the method on it: $date = new Carbon('first day of December 2008'); echo $date->addWeeks(2)

The string passed to Carbon::parse or to new Carbon can represent a relative time (next sunday, tomorrow, first day of next month, last year) or an absolute time (first day of December 2008, 2017-01-06). You can test if a string will produce a relative or absolute date with Carbon::hasRelativeKeywords().

$string = 'first day of next month';
if (strtotime($string) === false) {
    echo "'$string' is not a valid date/time string.";
} elseif (Carbon::hasRelativeKeywords($string)) {
    echo "'$string' is a relative valid date/time string, it will returns different dates depending on the current date.";
} else {
    echo "'$string' is an absolute date/time string, it will always returns the same date.";
}

To accompany now(), a few other static instantiation helpers exist to create widely known instances. The only thing to really notice here is that today(), tomorrow() and yesterday(), besides behaving as expected, all accept a timezone parameter and each has their time value set to 00:00:00.

$now = Carbon::now();
echo $now;                               // 2018-06-15 10:44:05
$today = Carbon::today();
echo $today;                             // 2018-06-15 00:00:00
$tomorrow = Carbon::tomorrow('Europe/London');
echo $tomorrow;                          // 2018-06-16 00:00:00
$yesterday = Carbon::yesterday();
echo $yesterday;                         // 2018-06-14 00:00:00

The next group of static helpers are the createXXX() helpers. Most of the static create functions allow you to provide as many or as few arguments as you want and will provide default values for all others. Generally default values are the current date, time or timezone. Higher values will wrap appropriately but invalid values will throw an InvalidArgumentException with an informative message. The message is obtained from an DateTime::getLastErrors() call.

Carbon::createFromDate($year, $month, $day, $tz);
Carbon::createFromTime($hour, $minute, $second, $tz);
Carbon::createFromTimeString("$hour:$minute:$second", $tz);
Carbon::create($year, $month, $day, $hour, $minute, $second, $tz);

createFromDate() will default the time to now. createFromTime() will default the date to today. create() will default any null parameter to the current respective value. As before, the $tz defaults to the current timezone and otherwise can be a DateTimeZone instance or simply a string timezone value. The only special case for default values (mimicking the underlying PHP library) occurs when an hour value is specified but no minutes or seconds, they will get defaulted to 0.

$xmasThisYear = Carbon::createFromDate(null, 12, 25);  // Year defaults to current year
$Y2K = Carbon::create(2000, 1, 1, 0, 0, 0); // equivalent to Carbon::createMidnightDate(2000, 1, 1)
$alsoY2K = Carbon::create(1999, 12, 31, 24);
$noonLondonTz = Carbon::createFromTime(12, 0, 0, 'Europe/London');
$teaTime = Carbon::createFromTimeString('17:00:00', 'Europe/London');

// A two digit minute could not be found
try { Carbon::create(1975, 5, 21, 22, -2, 0); } catch(InvalidArgumentException $x) { echo $x->getMessage(); }

Create exceptions occurs on such negative values but not on overflow, to get exceptions on overflow, use createSafe()

echo Carbon::create(2000, 1, 35, 13, 0, 0);
// 2000-02-04 13:00:00

try {
    Carbon::createSafe(2000, 1, 35, 13, 0, 0);
} catch (\Carbon\Exceptions\InvalidDateException $exp) {
    echo $exp->getMessage();
}
// day : 35 is not a valid value.

Note 1: 2018-02-29 produces also an exception while 2020-02-29 does not since 2020 is a leap year.

Note 2: Carbon::createSafe(2014, 3, 30, 1, 30, 0, 'Europe/London') also produces an exception since PHP 5.4 because this time is in an hour skipped by the daylight saving time, but before PHP 5.4, it will just create this invalid date as it has existed.

Carbon::createFromFormat($format, $time, $tz);

createFromFormat() is mostly a wrapper for the base php function DateTime::createFromFormat. The difference being again the $tz argument can be a DateTimeZone instance or a string timezone value. Also, if there are errors with the format this function will call the DateTime::getLastErrors() method and then throw a InvalidArgumentException with the errors as the message. If you look at the source for the createXX() functions above, they all make a call to createFromFormat().

echo Carbon::createFromFormat('Y-m-d H', '1975-05-21 22')->toDateTimeString(); // 1975-05-21 22:00:00

The final three create functions are for working with unix timestamps. The first will create a Carbon instance equal to the given timestamp and will set the timezone as well or default it to the current timezone. The second, createFromTimestampUTC(), is different in that the timezone will remain UTC (GMT). The second acts the same as Carbon::createFromFormat('@'.$timestamp) but I have just made it a little more explicit. The third, createFromTimestampMs(), accepts a timestamp in milliseconds instead of seconds. Negative timestamps are also allowed.

echo Carbon::createFromTimestamp(-1)->toDateTimeString();                                  // 1969-12-31 18:59:59
echo Carbon::createFromTimestamp(-1, 'Europe/London')->toDateTimeString();                 // 1970-01-01 00:59:59
echo Carbon::createFromTimestampUTC(-1)->toDateTimeString();                               // 1969-12-31 23:59:59
echo Carbon::createFromTimestampMs(1)->format('Y-m-d\TH:i:s.uP T');                        // 1969-12-31T19:00:00.001000-05:00 EST
echo Carbon::createFromTimestampMs(1, 'Europe/London')->format('Y-m-d\TH:i:s.uP T');       // 1970-01-01T01:00:00.001000+01:00 BST

You can also create a copy() of an existing Carbon instance. As expected the date, time and timezone values are all copied to the new instance.

$dt = Carbon::now();
echo $dt->diffInYears($dt->copy()->addYear());  // 1

// $dt was unchanged and still holds the value of Carbon:now()

You can use nowWithSameTz() on an existing Carbon instance to get a new instance at now in the same timezone.

$meeting = Carbon::createFromTime(19, 15, 00, 'Africa/Johannesburg');

// 19:15 in Johannesburg
echo 'Meeting starts at '.$meeting->format('H:i').' in Johannesburg.';                  // Meeting starts at 19:15 in Johannesburg.
// now in Johannesburg
echo "It's ".$meeting->nowWithSameTz()->format('H:i').' right now in Johannesburg.';    // It's 16:44 right now in Johannesburg.

Finally, if you find yourself inheriting a \DateTime instance from another library, fear not! You can create a Carbon instance via a friendly instance() method. Or use the even more flexible method make() which can return a new Carbon instance from a DateTime, Carbon or from a string, else it just returns null.

$dt = new \DateTime('first day of January 2008'); // <== instance from another API
$carbon = Carbon::instance($dt);
echo get_class($carbon);                               // 'Carbon\Carbon'
echo $carbon->toDateTimeString();                      // 2008-01-01 00:00:00

A quick note about microseconds. The PHP DateTime object allows you to set a microsecond value but ignores it for all of its date math. As of 1.12.0 Carbon now supports microseconds during instantiation or copy operations as well as by default with the format() method.

$dt = Carbon::parse('1975-05-21 22:23:00.123456');
echo $dt->micro;                                       // 123456
echo $dt->copy()->micro;                               // 123456

Before PHP 7.1 DateTime microseconds are not added to "now" instances and cannot be changed afterwards, this means:

$date = new DateTime('now');
echo $date->format('u');
// display current microtime in PHP >= 7.1 (expect a bug in PHP 7.1.3 only)
// display 000000 before PHP 7.1

$date = new DateTime('2001-01-01T00:00:00.123456Z');
echo $date->format('u');
// display 123456 in all PHP versions

$date->modify('00:00:00.987654');
echo $date->format('u');
// display 987654 in PHP >= 7.1
// display 123456 before PHP 7.1

To work around this limitation in Carbon, we append microseconds when calling now in PHP < 7.1, but this feature can be disabled on demand (no effect in PHP >= 7.1):

Carbon::useMicrosecondsFallback(false);
var_dump(Carbon::isMicrosecondsFallbackEnabled()); // false

echo Carbon::now()->micro; // 0 in PHP < 7.1, microtime in PHP >= 7.1

Carbon::useMicrosecondsFallback(true); // default value
var_dump(Carbon::isMicrosecondsFallbackEnabled()); // true

echo Carbon::now()->micro; // microtime in all PHP version

Ever need to loop through some dates to find the earliest or latest date? Didn't know what to set your initial maximum/minimum values to? There are now two helpers for this to make your decision simple:

echo Carbon::maxValue();                               // '9999-12-31 23:59:59'
echo Carbon::minValue();                               // '0001-01-01 00:00:00'

Min and max value mainly depends on the system (32 or 64 bit).

With a 32-bit OS system or 32-bit version of PHP (you can check it in PHP with PHP_INT_SIZE === 4), the minimum value is the 0-unix-timestamp (1970-01-01 00:00:00) and the maximum is the timestamp given by the constant PHP_INT_MAX.

With a 64-bit OS system and 64-bit version of PHP, the minimum is 01-01-01 00:00:00 and maximum is 9999-12-31 23:59:59.

Localization

Unfortunately the base class DateTime does not have any localization support. To begin localization support a formatLocalized($format) method was added. The implementation makes a call to strftime using the current instance timestamp. If you first set the current locale with PHP function setlocale() then the string returned will be formatted in the correct locale.

setlocale(LC_TIME, 'German');
echo $dt->formatLocalized('%A %d %B %Y');          // Mittwoch 21 Mai 1975
setlocale(LC_TIME, 'English');
echo $dt->formatLocalized('%A %d %B %Y');          // Wednesday 21 May 1975
setlocale(LC_TIME, ''); // reset locale

diffForHumans() has also been localized. You can set the Carbon locale by using the static Carbon::setLocale() function and get the current setting with Carbon::getLocale().

Carbon::setLocale('de');
echo Carbon::getLocale();                          // de
echo Carbon::now()->addYear()->diffForHumans();    // in 1 Jahr

Carbon::setLocale('en');
echo Carbon::getLocale();                          // en

Some languages require utf8 encoding to be printed (locale packages that does not ends with .UTF8 mainly). In this case you can use the static method Carbon::setUtf8() to encode the result of the formatLocalized() call to the utf8 charset.

setlocale(LC_TIME, 'Spanish');
$dt = Carbon::create(2016, 01, 06, 00, 00, 00);
Carbon::setUtf8(false);
echo $dt->formatLocalized('%A %d %B %Y');          // miťrcoles 06 enero 2016
Carbon::setUtf8(true);
echo $dt->formatLocalized('%A %d %B %Y');          // miércoles 06 enero 2016
Carbon::setUtf8(false);
setlocale(LC_TIME, '');

on Linux
If you have trouble with translations, check locales installed in your system (local and production).
locale -a to list locales enabled.
sudo locale-gen fr_FR.UTF-8 to install a new locale.
sudo dpkg-reconfigure locales to publish all locale enabled.
And reboot your system.

You can customize a existing language the following way:

Carbon::setLocale('en');
$translator = Carbon::getTranslator();
$translator->setMessages('en', array(
    'day' => ':count boring day|:count boring days',
));

$date1 = Carbon::create(2018, 1, 1, 0, 0, 0);
$date2 = Carbon::create(2018, 1, 4, 4, 0, 0);

echo $date1->diffForHumans($date2, true, false, 2); // 3 boring days 4 hours

$translator->resetMessages('en'); // reset language customizations for en language

Note that you also can use an other translator with Carbon::setTranslator($custom) as long as the given translator implements Symfony\Component\Translation\TranslatorInterface.

Testing Aids

The testing methods allow you to set a Carbon instance (real or mock) to be returned when a "now" instance is created. The provided instance will be returned specifically under the following conditions:

  • A call to the static now() method, ex. Carbon::now()
  • When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null)
  • When the string "now" is passed to the constructor or parse(), ex. new Carbon('now')
  • The given instance will also be used as default relative moment for diff methods

$knownDate = Carbon::create(2001, 5, 21, 12);          // create testing date
Carbon::setTestNow($knownDate);                        // set the mock (of course this could be a real mock object)
echo Carbon::getTestNow();                             // 2001-05-21 12:00:00
echo Carbon::now();                                    // 2001-05-21 12:00:00
echo new Carbon();                                     // 2001-05-21 12:00:00
echo Carbon::parse();                                  // 2001-05-21 12:00:00
echo new Carbon('now');                                // 2001-05-21 12:00:00
echo Carbon::parse('now');                             // 2001-05-21 12:00:00
echo Carbon::create(2001, 4, 21, 12)->diffForHumans(); // 1 month ago
var_dump(Carbon::hasTestNow());                        // bool(true)
Carbon::setTestNow();                                  // clear the mock
var_dump(Carbon::hasTestNow());                        // bool(false)
echo Carbon::now();                                    // 2018-06-15 10:44:05

A more meaning full example:

class SeasonalProduct
{
    protected $price;

    public function __construct($price)
    {
        $this->price = $price;
    }

    public function getPrice() {
        $multiplier = 1;
        if (Carbon::now()->month == 12) {
            $multiplier = 2;
        }

        return $this->price * $multiplier;
    }
}

$product = new SeasonalProduct(100);
Carbon::setTestNow(Carbon::parse('first day of March 2000'));
echo $product->getPrice();                                             // 100
Carbon::setTestNow(Carbon::parse('first day of December 2000'));
echo $product->getPrice();                                             // 200
Carbon::setTestNow(Carbon::parse('first day of May 2000'));
echo $product->getPrice();                                             // 100
Carbon::setTestNow();

Relative phrases are also mocked according to the given "now" instance.

$knownDate = Carbon::create(2001, 5, 21, 12);          // create testing date
Carbon::setTestNow($knownDate);                        // set the mock
echo new Carbon('tomorrow');                           // 2001-05-22 00:00:00  ... notice the time !
echo new Carbon('yesterday');                          // 2001-05-20 00:00:00
echo new Carbon('next wednesday');                     // 2001-05-23 00:00:00
echo new Carbon('last friday');                        // 2001-05-18 00:00:00
echo new Carbon('this thursday');                      // 2001-05-24 00:00:00
Carbon::setTestNow();                                  // always clear it !

The list of words that are considered to be relative modifiers are:

  • +
  • -
  • ago
  • first
  • next
  • last
  • this
  • today
  • tomorrow
  • yesterday

Be aware that similar to the next(), previous() and modify() methods some of these relative modifiers will set the time to 00:00:00.

Carbon::parse($time, $tz) and new Carbon($time, $tz) both can take a timezone as second argument.

echo Carbon::parse('2012-9-5 23:26:11.223', 'Europe/Paris')->timezone->getName(); // Europe/Paris

Getters

The getters are implemented via PHP's __get() method. This enables you to access the value as if it was a property rather than a function call.

$dt = Carbon::parse('2012-10-5 23:26:11.123789');

// These getters specifically return integers, ie intval()
var_dump($dt->year);                                         // int(2012)
var_dump($dt->month);                                        // int(10)
var_dump($dt->day);                                          // int(5)
var_dump($dt->hour);                                         // int(23)
var_dump($dt->minute);                                       // int(26)
var_dump($dt->second);                                       // int(11)
var_dump($dt->micro);                                        // int(123789)
// dayOfWeek returns a number between 0 (sunday) and 6 (saturday)
var_dump($dt->dayOfWeek);                                    // int(5)
// dayOfWeekIso returns a number between 1 (monday) and 7 (sunday)
var_dump($dt->dayOfWeekIso);                                 // int(5)
setlocale(LC_TIME, 'German');
var_dump($dt->englishDayOfWeek);                             // string(6) "Friday"
var_dump($dt->shortEnglishDayOfWeek);                        // string(3) "Fri"
var_dump($dt->localeDayOfWeek);                              // string(7) "Freitag"
var_dump($dt->shortLocaleDayOfWeek);                         // string(2) "Fr"
var_dump($dt->englishMonth);                                 // string(7) "October"
var_dump($dt->shortEnglishMonth);                            // string(3) "Oct"
var_dump($dt->localeMonth);                                  // string(7) "Oktober"
var_dump($dt->shortLocaleMonth);                             // string(3) "Okt"
setlocale(LC_TIME, '');
var_dump($dt->dayOfYear);                                    // int(278)
var_dump($dt->weekNumberInMonth);                            // int(1)
// weekNumberInMonth consider weeks from monday to sunday, so the week 1 will
// contain 1 day if the month start with a sunday, and up to 7 if it starts with a monday
var_dump($dt->weekOfMonth);                                  // int(1)
// weekOfMonth will returns 1 for the 7 first days of the month, then 2 from the 8th to
// the 14th, 3 from the 15th to the 21st, 4 from 22nd to 28th and 5 above
var_dump($dt->weekOfYear);                                   // int(40)
var_dump($dt->daysInMonth);                                  // int(31)
var_dump($dt->timestamp);                                    // int(1349493971)
var_dump(Carbon::createFromDate(1975, 5, 21)->age);          // int(43) calculated vs now in the same tz
var_dump($dt->quarter);                                      // int(4)

// Returns an int of seconds difference from UTC (+/- sign included)
var_dump(Carbon::createFromTimestampUTC(0)->offset);         // int(0)
var_dump(Carbon::createFromTimestamp(0)->offset);            // int(-18000)

// Returns an int of hours difference from UTC (+/- sign included)
var_dump(Carbon::createFromTimestamp(0)->offsetHours);       // int(-5)

// Indicates if day light savings time is on
var_dump(Carbon::createFromDate(2012, 1, 1)->dst);           // bool(false)
var_dump(Carbon::createFromDate(2012, 9, 1)->dst);           // bool(true)

// Indicates if the instance is in the same timezone as the local timezone
var_dump(Carbon::now()->local);                              // bool(true)
var_dump(Carbon::now('America/Vancouver')->local);           // bool(false)

// Indicates if the instance is in the UTC timezone
var_dump(Carbon::now()->utc);                                // bool(false)
var_dump(Carbon::now('Europe/London')->utc);                 // bool(false)
var_dump(Carbon::createFromTimestampUTC(0)->utc);            // bool(true)

// Gets the DateTimeZone instance
echo get_class(Carbon::now()->timezone);                     // DateTimeZone
echo get_class(Carbon::now()->tz);                           // DateTimeZone

// Gets the DateTimeZone instance name, shortcut for ->timezone->getName()
echo Carbon::now()->timezoneName;                            // America/Toronto
echo Carbon::now()->tzName;                                  // America/Toronto

Setters

The following setters are implemented via PHP's __set() method. Its good to take note here that none of the setters, with the obvious exception of explicitly setting the timezone, will change the timezone of the instance. Specifically, setting the timestamp will not set the corresponding timezone to UTC.

$dt = Carbon::now();

$dt->year = 1975;
$dt->month = 13;             // would force year++ and month = 1
$dt->month = 5;
$dt->day = 21;
$dt->hour = 22;
$dt->minute = 32;
$dt->second = 5;

$dt->timestamp = 169957925;  // This will not change the timezone

// Set the timezone via DateTimeZone instance or string
$dt->timezone = new DateTimeZone('Europe/London');
$dt->timezone = 'Europe/London';
$dt->tz = 'Europe/London';

Fluent Setters

No arguments are optional for the setters, but there are enough variety in the function definitions that you shouldn't need them anyway. Its good to take note here that none of the setters, with the obvious exception of explicitly setting the timezone, will change the timezone of the instance. Specifically, setting the timestamp will not set the corresponding timezone to UTC.

$dt = Carbon::now();

$dt->year(1975)->month(5)->day(21)->hour(22)->minute(32)->second(5)->toDateTimeString();
$dt->setDate(1975, 5, 21)->setTime(22, 32, 5)->toDateTimeString();
$dt->setDate(1975, 5, 21)->setTimeFromTimeString('22:32:05')->toDateTimeString();
$dt->setDateTime(1975, 5, 21, 22, 32, 5)->toDateTimeString();

$dt->timestamp(169957925)->timezone('Europe/London');

$dt->tz('America/Toronto')->setTimezone('America/Vancouver');

You also can set date and time separatly from other DateTime/Carbon objects:

$source1 = new Carbon('2010-05-16 22:40:10');

$dt = new Carbon('2001-01-01 01:01:01');
$dt->setTimeFrom($source1);

echo $dt; // 2001-01-01 22:40:10

$source2 = new DateTime('2013-09-01 09:22:56');

$dt->setDateFrom($source2);

echo $dt; // 2013-09-01 22:40:10

IsSet

The PHP function __isset() is implemented. This was done as some external systems (ex. Twig) validate the existence of a property before using it. This is done using the isset() or empty() method. You can read more about these on the PHP site: __isset(), isset(), empty().

var_dump(isset(Carbon::now()->iDoNotExist));       // bool(false)
var_dump(isset(Carbon::now()->hour));              // bool(true)
var_dump(empty(Carbon::now()->iDoNotExist));       // bool(true)
var_dump(empty(Carbon::now()->year));              // bool(false)

String Formatting

All of the available toXXXString() methods rely on the base class method DateTime::format(). You'll notice the __toString() method is defined which allows a Carbon instance to be printed as a pretty date time string when used in a string context.

$dt = Carbon::create(1975, 12, 25, 14, 15, 16);

var_dump($dt->toDateTimeString() == $dt);          // bool(true) => uses __toString()
echo $dt->toDateString();                          // 1975-12-25
echo $dt->toFormattedDateString();                 // Dec 25, 1975
echo $dt->toTimeString();                          // 14:15:16
echo $dt->toDateTimeString();                      // 1975-12-25 14:15:16
echo $dt->toDayDateTimeString();                   // Thu, Dec 25, 1975 2:15 PM

// ... of course format() is still available
echo $dt->format('l jS \\of F Y h:i:s A');         // Thursday 25th of December 1975 02:15:16 PM

// The reverse hasFormat method allows you to test if a string looks like a given format
var_dump($dt->hasFormat('Thursday 25th December 1975 02:15:16 PM', 'l jS F Y h:i:s A')); // bool(true)

You can also set the default __toString() format (which defaults to Y-m-d H:i:s) thats used when type juggling occurs.

Carbon::setToStringFormat('jS \o\f F, Y g:i:s a');
echo $dt;                                          // 25th of December, 1975 2:15:16 pm
Carbon::resetToStringFormat();
echo $dt;                                          // 1975-12-25 14:15:16

Note: For localization support see the Localization section.

Common Formats

The following are wrappers for the common formats provided in the DateTime class.

$dt = Carbon::createFromFormat('Y-m-d H:i:s.u', '2019-02-01 03:45:27.612584');

// $dt->toAtomString() is the same as $dt->format(DateTime::ATOM);
echo $dt->toAtomString();        // 2019-02-01T03:45:27-05:00
echo $dt->toCookieString();      // Friday, 01-Feb-2019 03:45:27 EST

echo $dt->toIso8601String();     // 2019-02-01T03:45:27-05:00
// Be aware we chose to use the full-extended format of the ISO 8601 norm
// Natively, DateTime::ISO8601 format is not compatible with ISO-8601 as it
// is explained here in the PHP documentation:
// https://php.net/manual/class.datetime.php#datetime.constants.iso8601
// We consider it as a PHP mistake and chose not to provide method for this
// format, but you still can use it this way:
echo $dt->format(DateTime::ISO8601); // 2019-02-01T03:45:27-0500

echo $dt->toIso8601ZuluString(); // 2019-02-01T08:45:27Z
echo $dt->toRfc822String();      // Fri, 01 Feb 19 03:45:27 -0500
echo $dt->toRfc850String();      // Friday, 01-Feb-19 03:45:27 EST
echo $dt->toRfc1036String();     // Fri, 01 Feb 19 03:45:27 -0500
echo $dt->toRfc1123String();     // Fri, 01 Feb 2019 03:45:27 -0500
echo $dt->toRfc2822String();     // Fri, 01 Feb 2019 03:45:27 -0500
echo $dt->toRfc3339String();     // 2019-02-01T03:45:27-05:00
echo $dt->toRfc7231String();     // Fri, 01 Feb 2019 08:45:27 GMT
echo $dt->toRssString();         // Fri, 01 Feb 2019 03:45:27 -0500
echo $dt->toW3cString();         // 2019-02-01T03:45:27-05:00

var_dump($dt->toArray());
/*
array(12) {
  ["year"]=>
  int(2019)
  ["month"]=>
  int(2)
  ["day"]=>
  int(1)
  ["dayOfWeek"]=>
  int(5)
  ["dayOfYear"]=>
  int(31)
  ["hour"]=>
  int(3)
  ["minute"]=>
  int(45)
  ["second"]=>
  int(27)
  ["micro"]=>
  int(612584)
  ["timestamp"]=>
  int(1549010727)
  ["formatted"]=>
  string(19) "2019-02-01 03:45:27"
  ["timezone"]=>
  object(DateTimeZone)#35 (2) {
    ["timezone_type"]=>
    int(3)
    ["timezone"]=>
    string(15) "America/Toronto"
  }
}
*/

Comparison

Simple comparison is offered up via the following functions. Remember that the comparison is done in the UTC timezone so things aren't always as they seem.

echo Carbon::now()->tzName;                        // America/Toronto
$first = Carbon::create(2012, 9, 5, 23, 26, 11);
$second = Carbon::create(2012, 9, 5, 20, 26, 11, 'America/Vancouver');

echo $first->toDateTimeString();                   // 2012-09-05 23:26:11
echo $first->tzName;                               // America/Toronto
echo $second->toDateTimeString();                  // 2012-09-05 20:26:11
echo $second->tzName;                              // America/Vancouver

var_dump($first->eq($second));                     // bool(true)
var_dump($first->ne($second));                     // bool(false)
var_dump($first->gt($second));                     // bool(false)
var_dump($first->gte($second));                    // bool(true)
var_dump($first->lt($second));                     // bool(false)
var_dump($first->lte($second));                    // bool(true)

$first->setDateTime(2012, 1, 1, 0, 0, 0);
$second->setDateTime(2012, 1, 1, 0, 0, 0);         // Remember tz is 'America/Vancouver'

var_dump($first->eq($second));                     // bool(false)
var_dump($first->ne($second));                     // bool(true)
var_dump($first->gt($second));                     // bool(false)
var_dump($first->gte($second));                    // bool(false)
var_dump($first->lt($second));                     // bool(true)
var_dump($first->lte($second));                    // bool(true)

// All have verbose aliases and PHP equivalent code:

var_dump($first->eq($second));                     // bool(false)
var_dump($first->equalTo($second));                // bool(false)
var_dump($first == $second);                       // bool(false)

var_dump($first->ne($second));                     // bool(true)
var_dump($first->notEqualTo($second));             // bool(true)
var_dump($first != $second);                       // bool(true)

var_dump($first->gt($second));                     // bool(false)
var_dump($first->greaterThan($second));            // bool(false)
var_dump($first > $second);                        // bool(false)

var_dump($first->gte($second));                    // bool(false)
var_dump($first->greaterThanOrEqualTo($second));   // bool(false)
var_dump($first >= $second);                       // bool(false)

var_dump($first->lt($second));                     // bool(true)
var_dump($first->lessThan($second));               // bool(true)
var_dump($first < $second);                        // bool(true)

var_dump($first->lte($second));                    // bool(true)
var_dump($first->lessThanOrEqualTo($second));      // bool(true)
var_dump($first <= $second);                       // bool(true)

Those methods use natural comparisons offered by PHP $date1 == $date2 so all of them will ignore milli/micro-seconds before PHP 7.1, then take them into account starting with 7.1.

To determine if the current instance is between two other instances you can use the aptly named between() method. The third parameter indicates if an equal to comparison should be done. The default is true which determines if its between or equal to the boundaries.

$first = Carbon::create(2012, 9, 5, 1);
$second = Carbon::create(2012, 9, 5, 5);
var_dump(Carbon::create(2012, 9, 5, 3)->between($first, $second));          // bool(true)
var_dump(Carbon::create(2012, 9, 5, 5)->between($first, $second));          // bool(true)
var_dump(Carbon::create(2012, 9, 5, 5)->between($first, $second, false));   // bool(false)

Woah! Did you forget min() and max() ? Nope. That is covered as well by the suitably named min() and max() methods or minimum() and maximum() aliases. As usual the default parameter is now if null is specified.

$dt1 = Carbon::createMidnightDate(2012, 1, 1);
$dt2 = Carbon::createMidnightDate(2014, 1, 30);
echo $dt1->min($dt2);                              // 2012-01-01 00:00:00
echo $dt1->minimum($dt2);                          // 2012-01-01 00:00:00

$dt1 = Carbon::createMidnightDate(2012, 1, 1);
$dt2 = Carbon::createMidnightDate(2014, 1, 30);
echo $dt1->max($dt2);                              // 2014-01-30 00:00:00
echo $dt1->maximum($dt2);                          // 2014-01-30 00:00:00

// now is the default param
$dt1 = Carbon::createMidnightDate(2000, 1, 1);
echo $dt1->max();                                  // 2018-06-15 10:44:05
echo $dt1->maximum();                              // 2018-06-15 10:44:05

$dt1 = Carbon::createMidnightDate(2010, 4, 1);
$dt2 = Carbon::createMidnightDate(2010, 3, 28);
$dt3 = Carbon::createMidnightDate(2010, 4, 16);

// returns the closest of two date (no matter before or after)
echo $dt1->closest($dt2, $dt3);                    // 2010-03-28 00:00:00
echo $dt2->closest($dt1, $dt3);                    // 2010-04-01 00:00:00
echo $dt3->closest($dt2, $dt1);                    // 2010-04-01 00:00:00

// returns the farthest of two date (no matter before or after)
echo $dt1->farthest($dt2, $dt3);                   // 2010-04-16 00:00:00
echo $dt2->farthest($dt1, $dt3);                   // 2010-04-16 00:00:00
echo $dt3->farthest($dt2, $dt1);                   // 2010-03-28 00:00:00

To handle the most used cases there are some simple helper functions that hopefully are obvious from their names. For the methods that compare to now() (ex. isToday()) in some manner, the now() is created in the same timezone as the instance.

$dt = Carbon::now();
$dt2 = Carbon::createFromDate(1987, 4, 23);

$dt->isSameAs('w', $dt2); // w is the date of the week, so this will return true if $dt and $dt2
                          // the same day of week (both monday or both sunday, etc.)
                          // you can use any format and combine as much as you want.
$dt->isFuture();
$dt->isPast();

$dt->isSameYear($dt2);
$dt->isCurrentYear();
$dt->isNextYear();
$dt->isLastYear();
$dt->isLongYear(); // see https://en.wikipedia.org/wiki/ISO_8601#Week_dates
$dt->isLeapYear();

$dt->isSameQuarter($dt2); // same quarter (3 months) no matter the year of the given date
$dt->isSameQuarter($dt2, true); // same quarter of the same year of the given date
/*
    Alternatively, you can run Carbon::compareYearWithMonth() to compare both quarter and year by default,
    In this case you can use $dt->isSameQuarter($dt2, false) to compare ignoring the year
    Run Carbon::compareYearWithMonth(false) to reset to the default behavior
    Run Carbon::shouldCompareYearWithMonth() to get the current setting
*/
$dt->isCurrentQuarter();
$dt->isNextQuarter(); // date is in the next quarter
$dt->isLastQuarter(); // in previous quarter

$dt->isSameMonth($dt2); // same month no matter the year of the given date
$dt->isSameMonth($dt2, true); // same month of the same year of the given date
/*
    As for isSameQuarter, you can run Carbon::compareYearWithMonth() to compare both month and year by default,
    In this case you can use $dt->isSameMonth($dt2, false) to compare ignoring the year
    Run Carbon::compareYearWithMonth(false) to reset to the default behavior
    Run Carbon::shouldCompareYearWithMonth() to get the current setting
*/
$dt->isCurrentMonth();
$dt->isNextMonth();
$dt->isLastMonth();

$dt->isWeekday();
$dt->isWeekend();
$dt->isMonday();
$dt->isTuesday();
$dt->isWednesday();
$dt->isThursday();
$dt->isFriday();
$dt->isSaturday();
$dt->isSunday();
$dt->isDayOfWeek(Carbon::SATURDAY); // is a saturday
$dt->isLastOfMonth(); // is the last day of the month

$dt->isSameDay($dt2); // Same day of same month of same year
$dt->isCurrentDay();
$dt->isYesterday();
$dt->isToday();
$dt->isTomorrow();
$dt->isNextWeek();
$dt->isLastWeek();

$dt->isSameHour($dt2);
$dt->isCurrentHour();
$dt->isSameMinute($dt2);
$dt->isCurrentMinute();
$dt->isSameSecond($dt2);
$dt->isCurrentSecond();

$dt->isStartOfDay(); // check if hour is 00:00:00
$dt->isMidnight(); // check if hour is 00:00:00 (isStartOfDay alias)
$dt->isEndOfDay(); // check if hour is 23:59:59
$dt->isMidday(); // check if hour is 12:00:00 (or other midday hour set with Carbon::setMidDayAt())
$born = Carbon::createFromDate(1987, 4, 23);
$noCake = Carbon::createFromDate(2014, 9, 26);
$yesCake = Carbon::createFromDate(2014, 4, 23);
$overTheHill = Carbon::now()->subYears(50);
var_dump($born->isBirthday($noCake));              // bool(false)
var_dump($born->isBirthday($yesCake));             // bool(true)
var_dump($overTheHill->isBirthday());              // bool(true) -> default compare it to today!

Addition and Subtraction

The default DateTime provides a couple of different methods for easily adding and subtracting time. There is modify(), add() and sub(). modify() takes a magical date/time format string, 'last day of next month', that it parses and applies the modification while add() and sub() use a DateInterval class thats not so obvious, new \DateInterval('P6YT5M'). Hopefully using these fluent functions will be more clear and easier to read after not seeing your code for a few weeks. But of course I don't make you choose since the base class functions are still available.

$dt = Carbon::create(2012, 1, 31, 0);

echo $dt->toDateTimeString();            // 2012-01-31 00:00:00

echo $dt->addCenturies(5);               // 2512-01-31 00:00:00
echo $dt->addCentury();                  // 2612-01-31 00:00:00
echo $dt->subCentury();                  // 2512-01-31 00:00:00
echo $dt->subCenturies(5);               // 2012-01-31 00:00:00

echo $dt->addYears(5);                   // 2017-01-31 00:00:00
echo $dt->addYear();                     // 2018-01-31 00:00:00
echo $dt->subYear();                     // 2017-01-31 00:00:00
echo $dt->subYears(5);                   // 2012-01-31 00:00:00

echo $dt->addQuarters(2);                // 2012-07-31 00:00:00
echo $dt->addQuarter();                  // 2012-10-31 00:00:00
echo $dt->subQuarter();                  // 2012-07-31 00:00:00
echo $dt->subQuarters(2);                // 2012-01-31 00:00:00

echo $dt->addMonths(60);                 // 2017-01-31 00:00:00
echo $dt->addMonth();                    // 2017-03-03 00:00:00 equivalent of $dt->month($dt->month + 1); so it wraps
echo $dt->subMonth();                    // 2017-02-03 00:00:00
echo $dt->subMonths(60);                 // 2012-02-03 00:00:00

echo $dt->addDays(29);                   // 2012-03-03 00:00:00
echo $dt->addDay();                      // 2012-03-04 00:00:00
echo $dt->subDay();                      // 2012-03-03 00:00:00
echo $dt->subDays(29);                   // 2012-02-03 00:00:00

echo $dt->addWeekdays(4);                // 2012-02-09 00:00:00
echo $dt->addWeekday();                  // 2012-02-10 00:00:00
echo $dt->subWeekday();                  // 2012-02-09 00:00:00
echo $dt->subWeekdays(4);                // 2012-02-03 00:00:00

echo $dt->addWeeks(3);                   // 2012-02-24 00:00:00
echo $dt->addWeek();                     // 2012-03-02 00:00:00
echo $dt->subWeek();                     // 2012-02-24 00:00:00
echo $dt->subWeeks(3);                   // 2012-02-03 00:00:00

echo $dt->addHours(24);                  // 2012-02-04 00:00:00
echo $dt->addHour();                     // 2012-02-04 01:00:00
echo $dt->subHour();                     // 2012-02-04 00:00:00
echo $dt->subHours(24);                  // 2012-02-03 00:00:00

echo $dt->addMinutes(61);                // 2012-02-03 01:01:00
echo $dt->addMinute();                   // 2012-02-03 01:02:00
echo $dt->subMinute();                   // 2012-02-03 01:01:00
echo $dt->subMinutes(61);                // 2012-02-03 00:00:00

echo $dt->addSeconds(61);                // 2012-02-03 00:01:01
echo $dt->addSecond();                   // 2012-02-03 00:01:02
echo $dt->subSecond();                   // 2012-02-03 00:01:01
echo $dt->subSeconds(61);                // 2012-02-03 00:00:00

For fun you can also pass negative values to addXXX(), in fact that's how subXXX() is implemented.

P.S. Don't worry if you forget and use addDay(5) or subYear(3), I have your back ;)

By default, Carbon relies on the underlying parent class PHP DateTime behavior. As a result adding or subtracting months can overflow, example:

$dt = Carbon::create(2017, 1, 31, 0);

echo $dt->copy()->addMonth();            // 2017-03-03 00:00:00
echo $dt->copy()->subMonths(2);          // 2016-12-01 00:00:00

You can prevent the overflow with Carbon::useMonthsOverflow(false)

Carbon::useMonthsOverflow(false);

$dt = Carbon::createMidnightDate(2017, 1, 31);

echo $dt->copy()->addMonth();            // 2017-02-28 00:00:00
echo $dt->copy()->subMonths(2);          // 2016-11-30 00:00:00

// Call the method with true to allow overflow again
Carbon::resetMonthsOverflow(); // same as Carbon::useMonthsOverflow(true);

The method Carbon::shouldOverflowMonths() allows you to know if the overflow is currently enabled. You also can use ->addMonthsNoOverflow, ->subMonthsNoOverflow, ->addMonthsWithOverflow and ->subMonthsWithOverflow (or the singular methods with no s to "month") to explicitly add/sub months with or without overflow no matter the current mode.

Carbon::useMonthsOverflow(false);

$dt = Carbon::createMidnightDate(2017, 1, 31);

echo $dt->copy()->addMonthWithOverflow();          // 2017-03-03 00:00:00
// plural addMonthsWithOverflow() method is also available
echo $dt->copy()->subMonthsWithOverflow(2);        // 2016-12-01 00:00:00
// singular subMonthWithOverflow() method is also available
echo $dt->copy()->addMonthNoOverflow();            // 2017-02-28 00:00:00
// plural addMonthsNoOverflow() method is also available
echo $dt->copy()->subMonthsNoOverflow(2);          // 2016-11-30 00:00:00
// singular subMonthNoOverflow() method is also available

echo $dt->copy()->addMonth();                      // 2017-02-28 00:00:00
echo $dt->copy()->subMonths(2);                    // 2016-11-30 00:00:00

Carbon::useMonthsOverflow(true);

$dt = Carbon::createMidnightDate(2017, 1, 31);

echo $dt->copy()->addMonthWithOverflow();          // 2017-03-03 00:00:00
echo $dt->copy()->subMonthsWithOverflow(2);        // 2016-12-01 00:00:00
echo $dt->copy()->addMonthNoOverflow();            // 2017-02-28 00:00:00
echo $dt->copy()->subMonthsNoOverflow(2);          // 2016-11-30 00:00:00

echo $dt->copy()->addMonth();                      // 2017-03-03 00:00:00
echo $dt->copy()->subMonths(2);                    // 2016-12-01 00:00:00

Carbon::resetMonthsOverflow();

From version 1.23.0, overflow control is also available on years:

Carbon::useYearsOverflow(false);

$dt = Carbon::createMidnightDate(2020, 2, 29);

var_dump(Carbon::shouldOverflowYears());           // bool(false)

echo $dt->copy()->addYearWithOverflow();           // 2021-03-01 00:00:00
// plural addYearsWithOverflow() method is also available
echo $dt->copy()->subYearsWithOverflow(2);         // 2018-03-01 00:00:00
// singular subYearWithOverflow() method is also available
echo $dt->copy()->addYearNoOverflow();             // 2021-02-28 00:00:00
// plural addYearsNoOverflow() method is also available
echo $dt->copy()->subYearsNoOverflow(2);           // 2018-02-28 00:00:00
// singular subYearNoOverflow() method is also available

echo $dt->copy()->addYear();                       // 2021-02-28 00:00:00
echo $dt->copy()->subYears(2);                     // 2018-02-28 00:00:00

Carbon::useYearsOverflow(true);

$dt = Carbon::createMidnightDate(2020, 2, 29);

var_dump(Carbon::shouldOverflowYears());           // bool(true)

echo $dt->copy()->addYearWithOverflow();           // 2021-03-01 00:00:00
echo $dt->copy()->subYearsWithOverflow(2);         // 2018-03-01 00:00:00
echo $dt->copy()->addYearNoOverflow();             // 2021-02-28 00:00:00
echo $dt->copy()->subYearsNoOverflow(2);           // 2018-02-28 00:00:00

echo $dt->copy()->addYear();                       // 2021-03-01 00:00:00
echo $dt->copy()->subYears(2);                     // 2018-03-01 00:00:00

Carbon::resetYearsOverflow();

Difference

As Carbon extends DateTime it inherit its methods such as diff() that take a second date object as argument and returns a DateInterval instance.

We also provide diffAsCarbonInterval() act like diff() but returns a CarbonInterval instance. Check CarbonInterval chapter for more information. Carbon add diff methods for each unit too such as diffInYears(), diffInMonths() and so on. diffAsCarbonInterval() and diffIn*() methods all can take 2 optional arguments: date to compare with (if missing, now is used instead), and an absolute boolean option (true by default) that make the method return an absolute value no matter which date is greater than the other. If set to false, it returns negative value when the instance the method is called on is greater than the compared date (first argument or now). Note that diff() prototype is different: its first argument (the date) is mandatory and its second argument (the absolute option) defaults to false.

These functions always return the total difference expressed in the specified time requested. This differs from the base class diff() function where an interval of 122 seconds would be returned as 2 minutes and 2 seconds via a DateInterval instance. The diffInMinutes() function would simply return 2 while diffInSeconds() would return 122. All values are truncated and not rounded. Each function below has a default first parameter which is the Carbon instance to compare to, or null if you want to use now(). The 2nd parameter again is optional and indicates if you want the return value to be the absolute value or a relative value that might have a - (negative) sign if the passed in date is less than the current instance. This will default to true, return the absolute value.

echo Carbon::now('America/Vancouver')->diffInSeconds(Carbon::now('Europe/London')); // 0

$dtOttawa = Carbon::createMidnightDate(2000, 1, 1, 'America/Toronto');
$dtVancouver = Carbon::createMidnightDate(2000, 1, 1, 'America/Vancouver');
echo $dtOttawa->diffInHours($dtVancouver);                             // 3
echo $dtVancouver->diffInHours($dtOttawa);                             // 3

echo $dtOttawa->diffInHours($dtVancouver, false);                      // 3
echo $dtVancouver->diffInHours($dtOttawa, false);                      // -3

$dt = Carbon::createMidnightDate(2012, 1, 31);
echo $dt->diffInDays($dt->copy()->addMonth());                         // 31
echo $dt->diffInDays($dt->copy()->subMonth(), false);                  // -31

$dt = Carbon::createMidnightDate(2012, 4, 30);
echo $dt->diffInDays($dt->copy()->addMonth());                         // 30
echo $dt->diffInDays($dt->copy()->addWeek());                          // 7

$dt = Carbon::createMidnightDate(2012, 1, 1);
echo $dt->diffInMinutes($dt->copy()->addSeconds(59));                  // 0
echo $dt->diffInMinutes($dt->copy()->addSeconds(60));                  // 1
echo $dt->diffInMinutes($dt->copy()->addSeconds(119));                 // 1
echo $dt->diffInMinutes($dt->copy()->addSeconds(120));                 // 2

echo $dt->addSeconds(120)->secondsSinceMidnight();                     // 120

$interval = $dt->diffAsCarbonInterval($dt->copy()->subYears(3), false);
echo ($interval->invert ? 'minus ' : 'plus ') . $interval->years;      // minus 3

Important note about the daylight saving times (DST), by default PHP DateTime does not take DST into account, that means for example that a day with only 23 hours like March the 30th 2014 in London will be counted as 24 hours long.

$date = new DateTime('2014-03-30 00:00:00', new DateTimeZone('Europe/London')); // DST off
echo $date->modify('+25 hours')->format('H:i');                   // 01:00 (DST on, 24 hours only have been actually added)

Carbon follow this behavior too for add/sub/diff seconds/minutes/hours. But we provide methods to works with real hours using timestamp:

$date = new Carbon('2014-03-30 00:00:00', 'Europe/London');       // DST off
echo $date->addRealHours(25)->format('H:i');                      // 02:00 (DST on)
echo $date->diffInRealHours('2014-03-30 00:00:00');               // 25
echo $date->diffInHours('2014-03-30 00:00:00');                   // 26
echo $date->diffInRealMinutes('2014-03-30 00:00:00');             // 1500
echo $date->diffInMinutes('2014-03-30 00:00:00');                 // 1560
echo $date->diffInRealSeconds('2014-03-30 00:00:00');             // 90000
echo $date->diffInSeconds('2014-03-30 00:00:00');                 // 93600
echo $date->subRealHours(25)->format('H:i');                      // 00:00 (DST off)

The same way you can use addRealMinutes(), subRealMinutes(), addRealSeconds(), subRealSeconds(), and all their singular shortcuts: addRealHour(), subRealHour(), addRealMinute(), subRealMinute(), addRealSecond(), subRealSecond().

There are also special filter functions diffInDaysFiltered(), diffInHoursFiltered() and diffFiltered(), to help you filter the difference by days, hours or a custom interval. For example to count the weekend days between two instances:

$dt = Carbon::create(2014, 1, 1);
$dt2 = Carbon::create(2014, 12, 31);
$daysForExtraCoding = $dt->diffInDaysFiltered(function(Carbon $date) {
   return $date->isWeekend();
}, $dt2);

echo $daysForExtraCoding;      // 104

$dt = Carbon::create(2014, 1, 1)->endOfDay();
$dt2 = $dt->copy()->startOfDay();
$littleHandRotations = $dt->diffFiltered(CarbonInterval::minute(), function(Carbon $date) {
   return $date->minute === 0;
}, $dt2, true); // true as last parameter returns absolute value

echo $littleHandRotations;     // 24

$date = Carbon::now()->addSeconds(3666);

echo $date->diffInSeconds();                       // 3666
echo $date->diffInMinutes();                       // 61
echo $date->diffInHours();                         // 1
echo $date->diffInDays();                          // 0

$date = Carbon::create(2016, 1, 5, 22, 40, 32);

echo $date->secondsSinceMidnight();                // 81632
echo $date->secondsUntilEndOfDay();                // 4767

$date1 = Carbon::createMidnightDate(2016, 1, 5);
$date2 = Carbon::createMidnightDate(2017, 3, 15);

echo $date1->diffInDays($date2);                   // 435
echo $date1->diffInWeekdays($date2);               // 311
echo $date1->diffInWeekendDays($date2);            // 124
echo $date1->diffInWeeks($date2);                  // 62
echo $date1->diffInMonths($date2);                 // 14
echo $date1->diffInYears($date2);                  // 1

All diffIn*Filtered method take 1 callable filter as required parameter and a date object as optional second parameter, if missing, now is used. You may also pass true as third parameter to get absolute values.

For advanced handle of the week/weekend days, use the following tools:

echo implode(', ', Carbon::getDays());                       // Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday

$saturday = new Carbon('first saturday of 2019');
$sunday = new Carbon('first sunday of 2019');
$monday = new Carbon('first monday of 2019');

echo implode(', ', Carbon::getWeekendDays());                // 6, 0
var_dump($saturday->isWeekend());                            // bool(true)
var_dump($sunday->isWeekend());                              // bool(true)
var_dump($monday->isWeekend());                              // bool(false)

Carbon::setWeekendDays(array(
    Carbon::SUNDAY,
    Carbon::MONDAY,
));

echo implode(', ', Carbon::getWeekendDays());                // 0, 1
var_dump($saturday->isWeekend());                            // bool(false)
var_dump($sunday->isWeekend());                              // bool(true)
var_dump($monday->isWeekend());                              // bool(true)

Carbon::setWeekendDays(array(
    Carbon::SATURDAY,
    Carbon::SUNDAY,
));
// weekend days and start/end of week or not linked
Carbon::setWeekStartsAt(Carbon::FRIDAY);
Carbon::setWeekEndsAt(Carbon::WEDNESDAY); // and it does not need neither to precede the start

var_dump(Carbon::getWeekStartsAt() === Carbon::FRIDAY);      // bool(true)
var_dump(Carbon::getWeekEndsAt() === Carbon::WEDNESDAY);     // bool(true)
echo $saturday->copy()->startOfWeek()->toRfc850String();     // Friday, 01-Jun-18 00:00:00 EDT
echo $saturday->copy()->endOfWeek()->toRfc850String();       // Wednesday, 06-Jun-18 23:59:59 EDT

Carbon::setWeekStartsAt(Carbon::MONDAY);
Carbon::setWeekEndsAt(Carbon::SUNDAY);

echo $saturday->copy()->startOfWeek()->toRfc850String();     // Monday, 28-May-18 00:00:00 EDT
echo $saturday->copy()->endOfWeek()->toRfc850String();       // Sunday, 03-Jun-18 23:59:59 EDT

Difference for Humans

It is easier for humans to read 1 month ago compared to 30 days ago. This is a common function seen in most date libraries so I thought I would add it here as well. The lone argument for the function is the other Carbon instance to diff against, and of course it defaults to now() if not specified.

This method will add a phrase after the difference value relative to the instance and the passed in instance. There are 4 possibilities:

  • When comparing a value in the past to default now:
    • 1 hour ago
    • 5 months ago
  • When comparing a value in the future to default now:
    • 1 hour from now
    • 5 months from now
  • When comparing a value in the past to another value:
    • 1 hour before
    • 5 months before
  • When comparing a value in the future to another value:
    • 1 hour after
    • 5 months after

You may also pass true as a 2nd parameter to remove the modifiers ago, from now, etc : diffForHumans($other, true).

You may pass true as a 3rd parameter to use short syntax if available in the locale used : diffForHumans($other, false, true).

You may pass a number between 1 and 6 as a 4th parameter to get the difference in multiple parts (more precise diff) : diffForHumans($other, false, false, 4).

The $other instance can be a DateTime, a Carbon instance or any object that implement DateTimeInterface, if a string is passed it will be parsed to get a Carbon instance and if null is passed, Carbon::now() will be used instead.

// The most typical usage is for comments
// The instance is the date the comment was created and its being compared to default now()
echo Carbon::now()->subDays(5)->diffForHumans();               // 5 days ago

echo Carbon::now()->diffForHumans(Carbon::now()->subYear());   // 1 year after

$dt = Carbon::createFromDate(2011, 8, 1);

echo $dt->diffForHumans($dt->copy()->addMonth());                        // 1 month before
echo $dt->diffForHumans($dt->copy()->subMonth());                        // 1 month after

echo Carbon::now()->addSeconds(5)->diffForHumans();                      // 5 seconds from now

echo Carbon::now()->subDays(24)->diffForHumans();                        // 3 weeks ago
echo Carbon::now()->subDays(24)->diffForHumans(null, true);              // 3 weeks

echo Carbon::parse('2019-08-03')->diffForHumans('2019-08-13');           // 1 week before
echo Carbon::parse('2000-01-01 00:50:32')->diffForHumans('@946684800');  // 5 hours after

echo Carbon::create(2018, 2, 26, 4, 29, 43)->diffForHumans(Carbon::create(2016, 6, 21, 0, 0, 0), false, false, 6); // 1 year 8 months 5 days 4 hours 29 minutes 43 seconds after

You can also change the locale of the string using Carbon::setLocale('fr') before the diffForHumans() call. See the localization section for more detail.

Options for diffForHumans() can be enabled/disabled with:

Carbon::enableHumanDiffOption(Carbon::NO_ZERO_DIFF);
var_dump((bool) (Carbon::getHumanDiffOptions() & Carbon::NO_ZERO_DIFF)); // bool(true)
Carbon::disableHumanDiffOption(Carbon::NO_ZERO_DIFF);
var_dump((bool) (Carbon::getHumanDiffOptions() & Carbon::NO_ZERO_DIFF)); // bool(false)

Available options are:

  • Carbon::NO_ZERO_DIFF (enabled by default): turns empty diff into 1 second
  • Carbon::JUST_NOW disabled by default): turns diff from now to now into "just now"
  • Carbon::ONE_DAY_WORDS (disabled by default): turns "1 day from now/ago" to "yesterday/tomorrow"
  • Carbon::TWO_DAY_WORDS (disabled by default): turns "2 days from now/ago" to "before yesterday/after

Carbon::JUST_NOW, Carbon::ONE_DAY_WORDS and Carbon::TWO_DAY_WORDS are now only available with en and fr languages, other languages will fallback to previous behavior until missing translations are added.

Use the pipe operator to enable/disable multiple option at once, example: Carbon::ONE_DAY_WORDS | Carbon::TWO_DAY_WORDS

You also can use setHumanDiffOptions($options) to disable all options then activate only the ones passed as arguments.

Modifiers

These group of methods perform helpful modifications to the current instance. Most of them are self explanatory from their names... or at least should be. You'll also notice that the startOfXXX(), next() and previous() methods set the time to 00:00:00 and the endOfXXX() methods set the time to 23:59:59.

The only one slightly different is the average() function. It moves your instance to the middle date between itself and the provided Carbon argument.

$dt = Carbon::create(2012, 1, 31, 15, 32, 45);
echo $dt->startOfMinute();                         // 2012-01-31 15:32:00

$dt = Carbon::create(2012, 1, 31, 15, 32, 45);
echo $dt->endOfMinute();                           // 2012-01-31 15:32:59

$dt = Carbon::create(2012, 1, 31, 15, 32, 45);
echo $dt->startOfHour();                           // 2012-01-31 15:00:00

$dt = Carbon::create(2012, 1, 31, 15, 32, 45);
echo $dt->endOfHour();                             // 2012-01-31 15:59:59

$dt = Carbon::create(2012, 1, 31, 15, 32, 45);
echo Carbon::getMidDayAt();                        // 12
echo $dt->midDay();                                // 2012-01-31 12:00:00
Carbon::setMidDayAt(13);                
echo Carbon::getMidDayAt();                        // 13
echo $dt->midDay();                                // 2012-01-31 13:00:00
Carbon::setMidDayAt(12);                

$dt = Carbon::create(2012, 1, 31, 12, 0, 0);
echo $dt->startOfDay();                            // 2012-01-31 00:00:00

$dt = Carbon::create(2012, 1, 31, 12, 0, 0);
echo $dt->endOfDay();                              // 2012-01-31 23:59:59

$dt = Carbon::create(2012, 1, 31, 12, 0, 0);
echo $dt->startOfMonth();                          // 2012-01-01 00:00:00

$dt = Carbon::create(2012, 1, 31, 12, 0, 0);
echo $dt->endOfMonth();                            // 2012-01-31 23:59:59

$dt = Carbon::create(2012, 1, 31, 12, 0, 0);
echo $dt->startOfYear();                           // 2012-01-01 00:00:00

$dt = Carbon::create(2012, 1, 31, 12, 0, 0);
echo $dt->endOfYear();                             // 2012-12-31 23:59:59

$dt = Carbon::create(2012, 1, 31, 12, 0, 0);
echo $dt->startOfDecade();                         // 2010-01-01 00:00:00

$dt = Carbon::create(2012, 1, 31, 12, 0, 0);
echo $dt->endOfDecade();                           // 2019-12-31 23:59:59

$dt = Carbon::create(2012, 1, 31, 12, 0, 0);
echo $dt->startOfCentury();                        // 2001-01-01 00:00:00

$dt = Carbon::create(2012, 1, 31, 12, 0, 0);
echo $dt->endOfCentury();                          // 2100-12-31 23:59:59

$dt = Carbon::create(2012, 1, 31, 12, 0, 0);
echo $dt->startOfWeek();                           // 2012-01-30 00:00:00
var_dump($dt->dayOfWeek == Carbon::MONDAY);        // bool(true) : ISO8601 week starts on Monday

$dt = Carbon::create(2012, 1, 31, 12, 0, 0);
echo $dt->endOfWeek();                             // 2012-02-05 23:59:59
var_dump($dt->dayOfWeek == Carbon::SUNDAY);        // bool(true) : ISO8601 week ends on Sunday

$dt = Carbon::create(2012, 1, 31, 12, 0, 0);
echo $dt->next(Carbon::WEDNESDAY);                 // 2012-02-01 00:00:00
var_dump($dt->dayOfWeek == Carbon::WEDNESDAY);     // bool(true)

$dt = Carbon::create(2012, 1, 1, 12, 0, 0);
echo $dt->next();                                  // 2012-01-08 00:00:00

$dt = Carbon::create(2012, 1, 31, 12, 0, 0);
echo $dt->previous(Carbon::WEDNESDAY);             // 2012-01-25 00:00:00
var_dump($dt->dayOfWeek == Carbon::WEDNESDAY);     // bool(true)

$dt = Carbon::create(2012, 1, 1, 12, 0, 0);
echo $dt->previous();                              // 2011-12-25 00:00:00

$start = Carbon::create(2014, 1, 1, 0, 0, 0);
$end = Carbon::create(2014, 1, 30, 0, 0, 0);
echo $start->average($end);                        // 2014-01-15 12:00:00

echo Carbon::create(2014, 5, 30, 0, 0, 0)->firstOfMonth();                       // 2014-05-01 00:00:00
echo Carbon::create(2014, 5, 30, 0, 0, 0)->firstOfMonth(Carbon::MONDAY);         // 2014-05-05 00:00:00
echo Carbon::create(2014, 5, 30, 0, 0, 0)->lastOfMonth();                        // 2014-05-31 00:00:00
echo Carbon::create(2014, 5, 30, 0, 0, 0)->lastOfMonth(Carbon::TUESDAY);         // 2014-05-27 00:00:00
echo Carbon::create(2014, 5, 30, 0, 0, 0)->nthOfMonth(2, Carbon::SATURDAY);      // 2014-05-10 00:00:00

echo Carbon::create(2014, 5, 30, 0, 0, 0)->firstOfQuarter();                     // 2014-04-01 00:00:00
echo Carbon::create(2014, 5, 30, 0, 0, 0)->firstOfQuarter(Carbon::MONDAY);       // 2014-04-07 00:00:00
echo Carbon::create(2014, 5, 30, 0, 0, 0)->lastOfQuarter();                      // 2014-06-30 00:00:00
echo Carbon::create(2014, 5, 30, 0, 0, 0)->lastOfQuarter(Carbon::TUESDAY);       // 2014-06-24 00:00:00
echo Carbon::create(2014, 5, 30, 0, 0, 0)->nthOfQuarter(2, Carbon::SATURDAY);    // 2014-04-12 00:00:00
echo Carbon::create(2014, 5, 30, 0, 0, 0)->startOfQuarter();                     // 2014-04-01 00:00:00
echo Carbon::create(2014, 5, 30, 0, 0, 0)->endOfQuarter();                       // 2014-06-30 23:59:59

echo Carbon::create(2014, 5, 30, 0, 0, 0)->firstOfYear();                        // 2014-01-01 00:00:00
echo Carbon::create(2014, 5, 30, 0, 0, 0)->firstOfYear(Carbon::MONDAY);          // 2014-01-06 00:00:00
echo Carbon::create(2014, 5, 30, 0, 0, 0)->lastOfYear();                         // 2014-12-31 00:00:00
echo Carbon::create(2014, 5, 30, 0, 0, 0)->lastOfYear(Carbon::TUESDAY);          // 2014-12-30 00:00:00
echo Carbon::create(2014, 5, 30, 0, 0, 0)->nthOfYear(2, Carbon::SATURDAY);       // 2014-01-11 00:00:00

echo Carbon::create(2018, 2, 23, 0, 0, 0)->nextWeekday();                        // 2018-02-26 00:00:00
echo Carbon::create(2018, 2, 23, 0, 0, 0)->previousWeekday();                    // 2018-02-22 00:00:00
echo Carbon::create(2018, 2, 21, 0, 0, 0)->nextWeekendDay();                     // 2018-02-24 00:00:00
echo Carbon::create(2018, 2, 21, 0, 0, 0)->previousWeekendDay();                 // 2018-02-18 00:00:00

Constants

The following constants are defined in the Carbon class.

// These getters specifically return integers, ie intval()
var_dump(Carbon::SUNDAY);                          // int(0)
var_dump(Carbon::MONDAY);                          // int(1)
var_dump(Carbon::TUESDAY);                         // int(2)
var_dump(Carbon::WEDNESDAY);                       // int(3)
var_dump(Carbon::THURSDAY);                        // int(4)
var_dump(Carbon::FRIDAY);                          // int(5)
var_dump(Carbon::SATURDAY);                        // int(6)

var_dump(Carbon::YEARS_PER_CENTURY);               // int(100)
var_dump(Carbon::YEARS_PER_DECADE);                // int(10)
var_dump(Carbon::MONTHS_PER_YEAR);                 // int(12)
var_dump(Carbon::WEEKS_PER_YEAR);                  // int(52)
var_dump(Carbon::DAYS_PER_WEEK);                   // int(7)
var_dump(Carbon::HOURS_PER_DAY);                   // int(24)
var_dump(Carbon::MINUTES_PER_HOUR);                // int(60)
var_dump(Carbon::SECONDS_PER_MINUTE);              // int(60)

$dt = Carbon::createFromDate(2012, 10, 6);
if ($dt->dayOfWeek === Carbon::SATURDAY) {
    echo 'Place bets on Ottawa Senators Winning!';
}

Serialization

The Carbon instances can be serialized.

$dt = Carbon::create(2012, 12, 25, 20, 30, 00, 'Europe/Moscow');

echo serialize($dt);                                              // O:13:"Carbon\Carbon":3:{s:4:"date";s:26:"2012-12-25 20:30:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Moscow";}
// same as:
echo $dt->serialize();                                            // O:13:"Carbon\Carbon":3:{s:4:"date";s:26:"2012-12-25 20:30:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Moscow";}

$dt = 'O:13:"Carbon\Carbon":3:{s:4:"date";s:26:"2012-12-25 20:30:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Moscow";}';

echo unserialize($dt)->format('Y-m-d\TH:i:s.uP T');               // 2012-12-25T20:30:00.000000+04:00 MSK
// same as:
echo Carbon::fromSerialized($dt)->format('Y-m-d\TH:i:s.uP T');    // 2012-12-25T20:30:00.000000+04:00 MSK

JSON

The Carbon instances can be encoded to and decoded from JSON (these feature is available only from PHP 5.4+, see below notes about PHP 5.3).

$dt = Carbon::create(2012, 12, 25, 20, 30, 00, 'Europe/Moscow');
echo json_encode($dt);
// {"date":"2012-12-25 20:30:00.000000","timezone_type":3,"timezone":"Europe\/Moscow"}

$json = '{"date":"2012-12-25 20:30:00.000000","timezone_type":3,"timezone":"Europe\/Moscow"}';
$dt = Carbon::__set_state(json_decode($json, true));
echo $dt->format('Y-m-d\TH:i:s.uP T');
// 2012-12-25T20:30:00.000000+04:00 MSK

You can use serializeUsing() to customize the serialization.

$dt = Carbon::create(2012, 12, 25, 20, 30, 00, 'Europe/Moscow');
Carbon::serializeUsing(function ($date) {
    return $date->getTimestamp();
});
echo json_encode($dt);
/*
1356453000
*/

// Call serializeUsing with null to reset the serializer:
Carbon::serializeUsing(null);

The jsonSerialize() method returns the intermediate turned into string by `json_encode`, it can also allow you to work with PHP 5.3 compatibility.

$dt = Carbon::create(2012, 12, 25, 20, 30, 00, 'Europe/Moscow');
echo json_encode($dt->jsonSerialize());
// {"date":"2012-12-25 20:30:00.000000","timezone_type":3,"timezone":"Europe\/Moscow"}
// This is equivalent to the first json_encode example but works with PHP 5.3.

// And it can be used separately:
var_dump($dt->jsonSerialize());
// array(3) {
  ["date"]=>
  string(26) "2012-12-25 20:30:00.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(13) "Europe/Moscow"
}

Macro

You may be familiar with the macro concept if you are used to work with Laravel and objects such as response or collections. The Carbon macro() works the exact same way than the Laravel MacroableTrait one, it take a method name as first argument and a closure as second. This make the closure action available on all Carbon instance (and also as Carbon static method) as a method with the given name.

From PHP 5.4, $this can be used in closures to refer the current instance. For PHP 5.3 compatibility, we also add a `$self` attribute to the closure. Example:

Carbon::macro('diffFromYear', function ($year, $self = null) {
    // this chunk is needed to be compatible on both stand-alone Carbon with PHP versions < 5.4 and Laravel
    if (!isset($self) && isset($this)) {
        $self = $this;
    }
    // end of the compatibility chunk

    return $self->diffForHumans(Carbon::create($year, 1, 1, 0, 0, 0), false, false, 3);
});
echo Carbon::parse('2020-01-12 12:00:00')->diffFromYear(2019);                 // 1 year 1 week 4 days after

The compatibility chunk allow you to ensure a full compatibility for your macro. A macro on Illuminate\Support\Carbon (the Laravel wrapper class), $self will not be defined, and as mentioned above in PHP 5.3 $this will not be defined. So to make your macros work everywhere, just paste this if-statement testing if $this is defined but not $self and copying it if so, then use $self in your function body.

No matter if you omit some optional arguments as long as $self has exactly this name and is the last argument:

Carbon::macro('diffFromYear', function ($year, $absolute = false, $short = false, $parts = 1, $self = null) {
    // compatibility chunk
    if (!isset($self) && isset($this)) {
        $self = $this;
    }

    return $self->diffForHumans(Carbon::create($year, 1, 1, 0, 0, 0), $absolute, $short, $parts);
});

echo Carbon::parse('2020-01-12 12:00:00')->diffFromYear(2019);                 // 1 year after
echo Carbon::parse('2020-01-12 12:00:00')->diffFromYear(2019, true);           // 1 year
echo Carbon::parse('2020-01-12 12:00:00')->diffFromYear(2019, true, true);     // 1yr
echo Carbon::parse('2020-01-12 12:00:00')->diffFromYear(2019, true, true, 5);  // 1yr 1w 4d 12h

Macros can also be grouped in classes and be applied with mixin()

Class BeerDayCarbonMixin
{
    public function nextBeerDay()
    {
        return function ($self = null) {
            // compatibility chunk
            if (!isset($self) && isset($this)) {
                $self = $this;
            }

            return $self->modify('next wednesday');
        };
    }

    public function previousBeerDay()
    {
        return function ($self = null) {
            // compatibility chunk
            if (!isset($self) && isset($this)) {
                $self = $this;
            }

            return $self->modify('previous wednesday');
        };
    }
}

Carbon::mixin(new BeerDayCarbonMixin());

$date = Carbon::parse('First saturday of December 2018');

echo $date->previousBeerDay();                                                 // 2018-11-28 00:00:00
echo $date->nextBeerDay();                                                     // 2018-12-05 00:00:00

You can check if a macro (mixin included) is available with hasMacro()

var_dump(Carbon::hasMacro('previousBeerDay'));                                 // bool(true)
var_dump(Carbon::hasMacro('diffFromYear'));                                    // bool(true)
var_dump(Carbon::hasMacro('dontKnowWhat'));                                    // bool(false)

And guess what? all macro methods are also available on CarbonInterval and CarbonPeriod classes.

CarbonInterval::macro('twice', function ($self = null) {
    return $self->times(2);
});
echo CarbonInterval::day()->twice()->forHumans();                        // 2 days
echo CarbonInterval::hours(2)->minutes(15)->twice()->forHumans(true);    // 4h 30m

CarbonPeriod::macro('countWeekdays', function ($self = null) {
    return $self->filter('isWeekday')->count();
});
echo CarbonPeriod::create('2017-11-01', '2017-11-30')->countWeekdays();  // 22
echo CarbonPeriod::create('2017-12-01', '2017-12-31')->countWeekdays();  // 21

Here are some useful macros proposed by the community:

Carbon::macro('isHoliday', function ($self = null) {
    // compatibility chunk
    if (!isset($self) && isset($this)) {
        $self = $this;
    }

    return in_array($self->format('d/m'), [
        '25/12', // Christmas
        '01/01', // New Year
        // ...
    ]);
});
var_dump(Carbon::createMidnightDate(2012, 12, 25)->isHoliday());  // bool(true)
var_dump(Carbon::createMidnightDate(2017, 6, 25)->isHoliday());   // bool(false)
var_dump(Carbon::createMidnightDate(2021, 1, 1)->isHoliday());    // bool(true)

Credit: kylekatarnls (#116).

Check cmixin/business-day for a more complete business days handler.

Class CurrentDaysCarbonMixin
{
    /**
     * Get the all dates of week
     *
     * @return array
     */
    public static function getCurrentWeekDays()
    {
        return function ($self = null) {
            // compatibility chunk
            if (!isset($self) && isset($this)) {
                $self = $this;
            }

            $startOfWeek = ($self ?: static::now())->startOfWeek()->subDay();
            $weekDays = array();

            for ($i = 0; $i < static::DAYS_PER_WEEK; $i++) {
                $weekDays[] = $startOfWeek->addDay()->startOfDay()->copy();
            }

            return $weekDays;
        };
    }

    /**
     * Get the all dates of month
     *
     * @return array
     */
    public static function getCurrentMonthDays()
    {
        return function ($self = null) {
            // compatibility chunk
            if (!isset($self) && isset($this)) {
                $self = $this;
            }

            $startOfMonth = ($self ?: static::now())->startOfMonth()->subDay();
            $endOfMonth = ($self ?: static::now())->endOfMonth()->format('d');
            $monthDays = array();

            for ($i = 0; $i < $endOfMonth; $i++)
            {
                $monthDays[] = $startOfMonth->addDay()->startOfDay()->copy();
            }

            return $monthDays;
        };
    }
}

Carbon::mixin(new CurrentDaysCarbonMixin());

function dumpDateList($dates) {
    echo substr(implode(', ', $dates), 0, 100).'...';
}

dumpDateList(Carbon::getCurrentWeekDays());                       // 2018-06-11 00:00:00, 2018-06-12 00:00:00, 2018-06-13 00:00:00, 2018-06-14 00:00:00, 2018-06-15 00:00...
dumpDateList(Carbon::getCurrentMonthDays());                      // 2018-06-01 00:00:00, 2018-06-02 00:00:00, 2018-06-03 00:00:00, 2018-06-04 00:00:00, 2018-06-05 00:00...
dumpDateList(Carbon::now()->subMonth()->getCurrentWeekDays());    // 2018-05-14 00:00:00, 2018-05-15 00:00:00, 2018-05-16 00:00:00, 2018-05-17 00:00:00, 2018-05-18 00:00...
dumpDateList(Carbon::now()->subMonth()->getCurrentMonthDays());   // 2018-05-01 00:00:00, 2018-05-02 00:00:00, 2018-05-03 00:00:00, 2018-05-04 00:00:00, 2018-05-05 00:00...

Credit: meteguerlek (#1191).

Carbon::macro('toAtomStringWithNoTimezone', function ($self = null) {
    // compatibility chunk
    if (!isset($self) && isset($this)) {
        $self = $this;
    }

    return $self->format('Y-m-d\TH:i:s');
});
echo Carbon::parse('2021-06-16 20:08:34')->toAtomStringWithNoTimezone(); // 2021-06-16T20:08:34

Credit: afrojuju1 (#1063).

Carbon::macro('easterDate', function ($year) {
    return Carbon::createMidnightDate($year, 3, 21)->addDays(easter_days($year));
});
echo Carbon::easterDate(2015)->format('d/m'); // 05/04
echo Carbon::easterDate(2016)->format('d/m'); // 27/03
echo Carbon::easterDate(2017)->format('d/m'); // 16/04
echo Carbon::easterDate(2018)->format('d/m'); // 01/04
echo Carbon::easterDate(2019)->format('d/m'); // 21/04

Credit: andreisena, 36864 (#1052).

Check cmixin/business-day for a more complete holidays handler.

Carbon::macro('range', function ($startDate, $endDate) {
    return new DatePeriod($startDate, new DateInterval('P1D'), $endDate);
});
foreach (Carbon::range(Carbon::createMidnightDate(2019, 3, 28), Carbon::createMidnightDate(2019, 4, 3)) as $date) {
    echo "$date\n";
}
/*
2019-03-28 00:00:00
2019-03-29 00:00:00
2019-03-30 00:00:00
2019-03-31 00:00:00
2019-04-01 00:00:00
2019-04-02 00:00:00
*/

Credit: reinink (#132).

class UserTimezoneCarbonMixin
{
    public $userTimeZone;

    /**
     * Set user timezone, will be used before format function to apply current user timezone
     *
     * @param $timezone
     */
    public function setUserTimezone()
    {
        $mixin = $this;

        return function ($timezone) use ($mixin) {
            $mixin->userTimeZone = $timezone;
        };
    }

    /**
     * Returns date formatted according to given format.
     *
     * @param string $format
     *
     * @return string
     *
     * @link http://php.net/manual/en/datetime.format.php
     */
    public function tzFormat()
    {
        $mixin = $this;

        return function ($format, $self = null) use ($mixin) {
            // compatibility chunk
            if (!isset($self) && isset($this)) {
                $self = $this;
            }

            if (!is_null($mixin->userTimeZone)) {
                $self->timezone($mixin->userTimeZone);
            }

            return $self->format($format);
        };
    }
}

Carbon::mixin(new UserTimezoneCarbonMixin());

Carbon::setUserTimezone('Europe/Berlin');
echo Carbon::createFromTime(12, 0, 0, 'UTC')->tzFormat('H:i'); // 14:00
echo Carbon::createFromTime(15, 0, 0, 'UTC')->tzFormat('H:i'); // 17:00
Carbon::setUserTimezone('America/Toronto');
echo Carbon::createFromTime(12, 0, 0, 'UTC')->tzFormat('H:i'); // 08:00
echo Carbon::createFromTime(15, 0, 0, 'UTC')->tzFormat('H:i'); // 11:00

Credit: thiagocordeiro (#927).

CarbonInterval

The CarbonInterval class is inherited from the PHP DateInterval class.

<?php
class CarbonInterval extends \DateInterval
{
    // code here
}

You can create an instance in the following ways:

echo CarbonInterval::year();                           // 1 year
echo CarbonInterval::months(3);                        // 3 months
echo CarbonInterval::days(3)->seconds(32);             // 3 days 32 seconds
echo CarbonInterval::weeks(3);                         // 3 weeks
echo CarbonInterval::days(23);                         // 3 weeks 2 days
echo CarbonInterval::create(2, 0, 5, 1, 1, 2, 7);      // 2 years 5 weeks 1 day 1 hour 2 minutes 7 seconds

If you find yourself inheriting a \DateInterval instance from another library, fear not! You can create a CarbonInterval instance via a friendly instance() function.

$di = new \DateInterval('P1Y2M'); // <== instance from another API
$ci = CarbonInterval::instance($di);
echo get_class($ci);                                   // 'Carbon\CarbonInterval'
echo $ci;                                              // 1 year 2 months

Other helpers, but beware the implementation provides helpers to handle weeks but only days are saved. Weeks are calculated based on the total days of the current instance.

echo CarbonInterval::year()->years;                    // 1
echo CarbonInterval::year()->dayz;                     // 0
echo CarbonInterval::days(24)->dayz;                   // 24
echo CarbonInterval::days(24)->daysExcludeWeeks;       // 3
echo CarbonInterval::weeks(3)->days(14)->weeks;        // 2  <-- days setter overwrites the current value
echo CarbonInterval::weeks(3)->weeks;                  // 3
echo CarbonInterval::minutes(3)->weeksAndDays(2, 5);   // 2 weeks 5 days 3 minutes

CarbonInterval extends DateInterval and you can create both using ISO-8601 duration format:

$ci = CarbonInterval::create('P1Y2M3D');
$ci = new CarbonInterval('PT0S');

Carbon intervals can be created from human-friendly strings thanks to fromString() method.

CarbonInterval::fromString('2 minutes 15 seconds');
CarbonInterval::fromString('2m 15s'); // or abbreviated

Note that month abbreviate "mo" to distinguish from minutes and the whole syntax is not case sensitive.

It also has a handy forHumans(), which is mapped as the __toString() implementation, that prints the interval for humans.

CarbonInterval::setLocale('fr');
echo CarbonInterval::create(2, 1)->forHumans();        // 2 ans 1 mois
echo CarbonInterval::hour()->seconds(3);               // 1 heure 3 secondes
CarbonInterval::setLocale('en');

As you can see, you can change the locale of the string using CarbonInterval::setLocale('fr').

As for Carbon, you can use the make method to return a new instance of CarbonInterval from other interval or strings:

$dateInterval = new DateInterval('P2D');
$carbonInterval = CarbonInterval::month();
echo CarbonInterval::make($dateInterval)->forHumans();       // 2 days
echo CarbonInterval::make($carbonInterval)->forHumans();     // 1 month
echo CarbonInterval::make('PT3H')->forHumans();              // 3 hours
echo CarbonInterval::make('1h 15m')->forHumans();            // 1 hour 15 minutes
// Pass true to get short format
echo CarbonInterval::make('1h 15m')->forHumans(true);        // 1h 15m

Natively DateInterval add and multiply units separately, so:

$interval = CarbonInterval::make('7h 55m');
$interval->add(CarbonInterval::make('17h 35m'));
$interval->times(3);
echo $interval->forHumans(); // 72 hours 270 minutes

You get pure calculation from your input unit by unit. To cascade minutes into hours, hours into days etc. Use the cascade method:


echo $interval->forHumans();             // 72 hours 270 minutes
echo $interval->cascade()->forHumans();  // 3 days 4 hours 30 minutes

Default factors are:

  • 1 minute = 60 seconds
  • 1 hour = 60 minutes
  • 1 day = 24 hour
  • 1 week = 7 days
  • 1 month = 4 weeks
  • 1 year = 12 months

CarbonIntervals do not carry context so they cannot be more precise (no DST, no leap year, no real month length or year length consideration). But you can completely customize those factors. For example to deal with work time logs:

$cascades = CarbonInterval::getCascadeFactors(); // save initial factors

CarbonInterval::setCascadeFactors(array(
    'minute' => array(60, 'seconds'),
    'hour' => array(60, 'minutes'),
    'day' => array(8, 'hours'),
    'week' => array(5, 'days'),
    // in this example the cascade won't go farther than week unit
));

echo CarbonInterval::fromString('20h')->cascade()->forHumans();              // 2 days 4 hours
echo CarbonInterval::fromString('10d')->cascade()->forHumans();              // 2 weeks
echo CarbonInterval::fromString('3w 18d 53h 159m')->cascade()->forHumans();  // 7 weeks 4 days 7 hours 39 minutes

// You can see currently set factors with getFactor:
echo CarbonInterval::getFactor('minutes', /* per */ 'hour');                 // 60
echo CarbonInterval::getFactor('days', 'week');                              // 5

// And common factors can be get with short-cut methods:
echo CarbonInterval::getDaysPerWeek();                                       // 5
echo CarbonInterval::getHoursPerDay();                                       // 8
echo CarbonInterval::getMinutesPerHours();                                   // 60
echo CarbonInterval::getSecondsPerMinutes();                                 // 60

CarbonInterval::setCascadeFactors($cascades); // restore original factors

Is it possible to convert an interval into a given unit (using provided cascade factors).

echo CarbonInterval::days(3)->hours(5)->total('hours');    // 77
echo CarbonInterval::days(3)->hours(5)->totalHours;        // 77
echo CarbonInterval::months(6)->totalWeeks;                // 24
echo CarbonInterval::year()->totalDays;                    // 336

You can also get the ISO 8601 spec of the inverval with spec()

echo CarbonInterval::days(3)->hours(5)->spec(); // P3DT5H

It's also possible to get it from a DateInterval object since to the static helper:

echo CarbonInterval::getDateIntervalSpec(new DateInterval('P3DT6M10S')); // P3DT6M10S

List of date intervals can be sorted thanks to the compare() and compareDateIntervals() methods:

$halfDay = CarbonInterval::hours(12);
$oneDay = CarbonInterval::day();
$twoDay = CarbonInterval::days(2);

echo CarbonInterval::compareDateIntervals($oneDay, $oneDay);   // 0
echo $oneDay->compare($oneDay);                                // 0
echo CarbonInterval::compareDateIntervals($oneDay, $halfDay);  // 1
echo $oneDay->compare($halfDay);                               // 1
echo CarbonInterval::compareDateIntervals($oneDay, $twoDay);   // -1
echo $oneDay->compare($twoDay);                                // -1

$list = array($twoDay, $halfDay, $oneDay);
usort($list, array('Carbon\CarbonInterval', 'compareDateIntervals'));

echo implode(', ', $list);                                     // 12 hours, 1 day, 2 days

Last, a CarbonInterval instance can be converted into a CarbonPeriod instance by calling toPeriod() with complementary arguments.

I hear you ask what is a CarbonPeriod instance. Oh! Perfect transition to our next chapter.

CarbonPeriod

CarbonPeriod is a human-friendly version of the DatePeriod with many shortcuts.

// Create a new instance:
$period = new CarbonPeriod('2018-04-21', '3 days', '2018-04-27');
// Use static constructor:
$period = CarbonPeriod::create('2018-04-21', '3 days', '2018-04-27');
// Use the fluent setters:
$period = CarbonPeriod::since('2018-04-21')->days(3)->until('2018-04-27');
// Start from a CarbonInterval:
$period = CarbonInterval::days(3)->toPeriod('2018-04-21', '2018-04-27');

A CarbonPeriod can be constructed in a number of ways:

  • start date, end date and optional interval (by default 1 day),
  • start date, number of recurrences and optional interval,
  • an ISO 8601 interval specification.

Dates can be given as DateTime/Carbon instances, absolute strings like "2007-10-15 15:00" or relative strings, for example "next monday". Interval can be given as DateInterval/CarbonInterval instance, ISO 8601 interval specification like "P4D", or human readable string, for example "4 days".

Default constructor and create() methods are very forgiving in terms of argument types and order, so if you want to be more precise the fluent syntax is recommended. On the other hand you can pass dynamic array of values to createFromArray() which will do the job of constructing a new instance with the given array as a list of arguments.

CarbonPeriod implements the Iterator interface. It means that it can be passed directly to a foreach loop:

$period = CarbonPeriod::create('2018-04-21', '3 days', '2018-04-27');
foreach ($period as $key => $date) {
    if ($key) {
        echo ', ';
    }
    echo $date->format('m-d');
}
// 04-21, 04-24, 04-27

// Here is what happens under the hood:
$period->rewind(); // restart the iteration
while ($period->valid()) { // check if current item is valid
    if ($period->key()) { // echo comma if current key is greater than 0
        echo ', ';
    }
    echo $period->current()->format('m-d'); // echo current date
    $period->next(); // move to the next item
}
// 04-21, 04-24, 04-27

Parameters can be modified during the iteration:

$period = CarbonPeriod::create('2018-04-29', 7);
$dates = array();
foreach ($period as $key => $date) {
    if ($key === 3) {
        $period->invert()->start($date); // invert() is an alias for invertDateInterval()
    }
    $dates[] = $date->format('m-d');
}

echo implode(', ', $dates); // 04-29, 04-30, 05-01, 05-02, 05-01, 04-30, 04-29

Just as DatePeriod, the CarbonPeriod supports ISO 8601 time interval specification.

Note that the native DatePeriod treats recurrences as a number of times to repeat the interval. Thus it will give one less result when the start date is excluded. Introduction of custom filters in CarbonPeriod made it even more difficult to know the number of results. For that reason we changed the implementation slightly, and recurrences are treated as an overall limit for number of returned dates.

// Possible options are: CarbonPeriod::EXCLUDE_START_DATE | CarbonPeriod::EXCLUDE_END_DATE
// Default value is 0 which will have the same effect as when no options are given.
$period = CarbonPeriod::createFromIso('R4/2012-07-01T00:00:00Z/P7D', CarbonPeriod::EXCLUDE_START_DATE);
$dates = array();
foreach ($period as $date) {
    $dates[] = $date->format('m-d');
}

echo implode(', ', $dates); // 07-08, 07-15, 07-22, 07-29

You can retrieve data from the period with variety of getters:

$period = CarbonPeriod::create('2010-05-06', '2010-05-25', CarbonPeriod::EXCLUDE_START_DATE);

$exclude = $period->getOptions() & CarbonPeriod::EXCLUDE_START_DATE;

echo $period->getStartDate();            // 2010-05-06 00:00:00
echo $period->getEndDate();              // 2010-05-25 00:00:00
echo $period->getDateInterval();         // 1 day
echo $exclude ? 'exclude' : 'include';   // exclude

var_dump($period->isStartExcluded());    // bool(true)
var_dump($period->isEndExcluded());      // bool(false)

echo $period->toString();                // Every 1 day from 2010-05-06 to 2010-05-25
echo $period; /*implicit toString*/      // Every 1 day from 2010-05-06 to 2010-05-25

Additional getters let you access the results as an array:

$period = CarbonPeriod::create('2010-05-11', '2010-05-13');

echo $period->count();                   // 3, equivalent to count($period)
echo implode(', ', $period->toArray());  // 2010-05-11 00:00:00, 2010-05-12 00:00:00, 2010-05-13 00:00:00
echo $period->first();                   // 2010-05-11 00:00:00
echo $period->last();                    // 2010-05-13 00:00:00

Note that if you intend to work using the above functions it's a good idea to store the result of toArray() call to a variable and use it instead, because each call performs a full iteration internally.

To change the parameters you can use setter methods:

$period = CarbonPeriod::create('2010-05-01', '2010-05-14', CarbonPeriod::EXCLUDE_END_DATE);

$period->setStartDate('2010-05-11');
echo implode(', ', $period->toArray());  // 2010-05-11 00:00:00, 2010-05-12 00:00:00, 2010-05-13 00:00:00

// Second argument can be optionally used to exclude the date from the results.
$period->setStartDate('2010-05-11', false);
$period->setEndDate('2010-05-14', true);
echo implode(', ', $period->toArray());  // 2010-05-12 00:00:00, 2010-05-13 00:00:00, 2010-05-14 00:00:00

$period->setRecurrences(2);
echo implode(', ', $period->toArray());  // 2010-05-12 00:00:00, 2010-05-13 00:00:00

$period->setDateInterval('PT12H');
echo implode(', ', $period->toArray());  // 2010-05-11 12:00:00, 2010-05-12 00:00:00

You can change options using setOptions() to replace all options but you also can change them separately:

$period = CarbonPeriod::create('2010-05-06', '2010-05-25');

var_dump($period->isStartExcluded());    // bool(false)
var_dump($period->isEndExcluded());      // bool(false)

$period->toggleOptions(CarbonPeriod::EXCLUDE_START_DATE, true); // true, false or nothing to invert the option
var_dump($period->isStartExcluded());    // bool(true)
var_dump($period->isEndExcluded());      // bool(false) (unchanged)

$period->excludeEndDate();               // specify false to include, true or omit to exclude
var_dump($period->isStartExcluded());    // bool(true) (unchanged)
var_dump($period->isEndExcluded());      // bool(true)

$period->excludeStartDate(false);        // specify false to include, true or omit to exclude
var_dump($period->isStartExcluded());    // bool(false)
var_dump($period->isEndExcluded());      // bool(true)

As mentioned earlier, per ISO 8601 specification, recurrences is a number of times the interval should be repeated. The native DatePeriod will thus vary the number of returned dates depending on the exclusion of the start date. Meanwhile CarbonPeriod being more forgiving in terms of input and allowing custom filters, treats recurrences as an overall limit for number of returned dates:

$period = CarbonPeriod::createFromIso('R4/2012-07-01T00:00:00Z/P7D');
$days = array();
foreach ($period as $date) {
    $days[] = $date->format('d');
}

echo $period->getRecurrences();          // 4
echo implode(', ', $days);               // 01, 08, 15, 22

$days = array();
$period->setRecurrences(3)->excludeStartDate();
foreach ($period as $date) {
    $days[] = $date->format('d');
}

echo $period->getRecurrences();          // 3
echo implode(', ', $days);               // 08, 15, 22

$days = array();
$period = CarbonPeriod::recurrences(3)->sinceNow();
foreach ($period as $date) {
    $days[] = $date->format('Y-m-d');
}

echo implode(', ', $days);               // 2018-06-15, 2018-06-16, 2018-06-17

Dates returned by the DatePeriod can be easily filtered. Filters can be used for example to skip certain dates or iterate only over working days or weekends. A filter function should return true to accept a date, false to skip it but continue searching or CarbonPeriod::END_ITERATION to end the iteration.

$period = CarbonPeriod::between('2000-01-01', '2000-01-15');
$weekendFilter = function ($date) {
    return $date->isWeekend();
};
$period->filter($weekendFilter);

$days = array();
foreach ($period as $date) {
    $days[] = $date->format('m-d');
}
echo implode(', ', $days);                         // 01-01, 01-02, 01-08, 01-09, 01-15

You also can skip one or more value(s) inside the loop.

$period = CarbonPeriod::between('2000-01-01', '2000-01-10');
$days = array();
foreach ($period as $date) {
    $day = $date->format('m-d');
    $days[] = $day;
    if ($day === '01-04') {
        $period->skip(3);
    }
}
echo implode(', ', $days);                         // 01-01, 01-02, 01-03, 01-04, 01-08, 01-09, 01-10

getFilters() allow you to retrieve all the stored filters in a period. But be aware the recurrences limit and the end date will appear in the returned array as they are stored internally as filters.

$period = CarbonPeriod::end('2000-01-01')->recurrences(3);
var_export($period->getFilters());
/*
array (
  0 => 
  array (
    0 => 'Carbon\\CarbonPeriod::filterEndDate',
    1 => NULL,
  ),
  1 => 
  array (
    0 => 'Carbon\\CarbonPeriod::filterRecurrences',
    1 => NULL,
  ),
)
*/

Filters are stored in a stack and can be managed using a special set of methods:

$period = CarbonPeriod::between('2000-01-01', '2000-01-15');
$weekendFilter = function ($date) {
    return $date->isWeekend();
};

var_dump($period->hasFilter($weekendFilter));      // bool(false)
$period->addFilter($weekendFilter);
var_dump($period->hasFilter($weekendFilter));      // bool(true)
$period->removeFilter($weekendFilter);
var_dump($period->hasFilter($weekendFilter));      // bool(false)

// To avoid storing filters as variables you can name your filters:
$period->prependFilter(function ($date) {
    return $date->isWeekend();
}, 'weekend');

var_dump($period->hasFilter('weekend'));           // bool(true)
$period->removeFilter('weekend');
var_dump($period->hasFilter('weekend'));           // bool(false)

Order in which filters are added can have an impact on the performance and on the result, so you can use addFilter() to add a filter in the end of stack; and you can use prependFilter() to add one at the beginning. You can even use setFilters() to replace all filters. Note that you'll have to keep correct format of the stack and remember about internal filters for recurrences limit and end date. Alternatively you can use resetFilters() method and then add new filters one by one.

For example, when you add a custom filter that limits the number of attempted dates, the result will be different if you add it before or after the weekday filter.

// Note that you can pass a name of any Carbon method starting with "is", including macros
$period = CarbonPeriod::between('2018-05-03', '2018-05-25')->filter('isWeekday');

$attempts = 0;
$attemptsFilter = function () use (&$attempts) {
    return ++$attempts <= 5 ? true : CarbonPeriod::END_ITERATION;
};

$period->prependFilter($attemptsFilter, 'attempts');
$days = array();
foreach ($period as $date) {
    $days[] = $date->format('m-d');
}
echo implode(', ', $days);                         // 05-03, 05-04, 05-07

$attempts = 0;

$period->removeFilter($attemptsFilter)->addFilter($attemptsFilter, 'attempts');
$days = array();
foreach ($period as $date) {
    $days[] = $date->format('m-d');
}
echo implode(', ', $days);                         // 05-03, 05-04, 05-07, 05-08, 05-09

Note that the built-in recurrences filter doesn't work this way. It is instead based on the current key which is incremented only once per item, no matter how many dates have to be checked before a valid date is found. This trick makes it work the same both if you put it at the beginning or at the end of the stack.

A number of aliases has been added to simplify building the CarbonPeriod:

// "start", "since", "sinceNow":
CarbonPeriod::start('2017-03-10') == CarbonPeriod::create()->setStartDate('2017-03-10');
// Same with optional boolean argument $inclusive to change the option about include/exclude start date:
CarbonPeriod::start('2017-03-10', true) == CarbonPeriod::create()->setStartDate('2017-03-10', true);
// "end", "until", "untilNow":
CarbonPeriod::end('2017-03-20') == CarbonPeriod::create()->setEndDate('2017-03-20');
// Same with optional boolean argument $inclusive to change the option about include/exclude end date:
CarbonPeriod::end('2017-03-20', true) == CarbonPeriod::create()->setEndDate('2017-03-20', true);
// "dates", "between":
CarbonPeriod::dates(..., ...) == CarbonPeriod::create()->setDates(..., ...);
// "recurrences", "times":
CarbonPeriod::recurrences(5) == CarbonPeriod::create()->setRecurrences(5);
// "options":
CarbonPeriod::options(...) == CarbonPeriod::create()->setOptions(...);
// "toggle":
CarbonPeriod::toggle(..., true) == CarbonPeriod::create()->toggleOptions(..., true);
// "filter", "push":
CarbonPeriod::filter(...) == CarbonPeriod::create()->addFilter(...);
// "prepend":
CarbonPeriod::prepend(...) == CarbonPeriod::create()->prependFilter(...);
// "filters":
CarbonPeriod::filters(...) == CarbonPeriod::create()->setFilters(...);
// "interval", "each", "every", "step", "stepBy":
CarbonPeriod::interval(...) == CarbonPeriod::create()->setDateInterval(...);
// "invert":
CarbonPeriod::invert() == CarbonPeriod::create()->invertDateInterval();
// "year", "months", "month", "weeks", "week", "days", "dayz", "day",
// "hours", "hour", "minutes", "minute", "seconds", "second":
CarbonPeriod::hours(5) == CarbonPeriod::create()->setDateInterval(new CarbonInterval::hours(5));

CarbonPeriod can be easily converted to a human readable string and ISO 8601 specification:

$period = CarbonPeriod::create('2000-01-01 12:00', '3 days 12 hours', '2000-01-15 12:00');
echo $period->toString();            // Every 3 days 12 hours from 2000-01-01 12:00:00 to 2000-01-15 12:00:00
echo $period->toIso8601String();     // 2000-01-01T12:00:00-05:00/P3DT12H/2000-01-15T12:00:00-05:00