Carbon macros

Optional Carbon support. Turn any Carbon instance into a Jalali, Hijri or Hebrew date, and create Carbon dates from calendar parts. The page lists every macro with its return type and errors.

On this page
  1. What this gives you
  2. How registration works
  3. Macro reference
  4. From Carbon to a calendar
  5. From calendar parts to Carbon
  6. Errors
  7. Good to know

What this gives you#

If nesbot/carbon is installed, RTLY-Kit adds a few macros to Carbon\Carbon and Carbon\CarbonImmutable. They move you between Carbon and the calendar classes in one call, in both directions. Carbon is optional. Without it nothing is registered and nothing breaks. Laravel apps already ship Carbon, so the macros are there without any extra step (see Laravel setup).

Before you start. Install RTLY-Kit (Installation) and Carbon 3 (composer require nesbot/carbon).

How registration works#

Composer loads the package's helper file on autoload. That file starts the integration. It checks whether the Carbon classes exist and, if they do, registers the macros once. You call nothing yourself:

<?php
require 'vendor/autoload.php';

use Carbon\Carbon;
use Carbon\CarbonImmutable;
use RtlyKit\Calendar\HijriVariant;
use RtlyKit\Calendar\Jalali;

var_dump(Carbon::hasMacro('toJalali'));   // bool(true)
Install Carbon with Composer. The macros are registered when Composer's autoloader starts and can find the Carbon classes. A Carbon copy that is not on the same autoloader, such as a bundled phar, is not found. The registration classes are internal. The macro names are part of the public API.

Macro reference#

MacroKindReturnsNotes
toJalali()instanceJalaliSame moment and time zone as the Carbon object
jformat($format = 'Y/m/d H:i:s')instancestringShortcut for toJalali()->format($format). Tokens as in Jalali formatting
toHijri(?HijriVariant $variant = null)instanceHijriUmm al-Qura unless you give a variant
toHebrew()instanceHebrew
createFromJalali($y, $m, $d, $h = 0, $i = 0, $s = 0, $tz = null)staticthe class you called it onCarbon:: returns Carbon. CarbonImmutable:: returns CarbonImmutable
createFromHijri($y, $m, $d, $h = 0, $i = 0, $s = 0, $tz = null, ?HijriVariant $variant = null)staticthe class you called it onThe variant defaults to Umm al-Qura
createFromHebrew($y, $m, $d, $h = 0, $i = 0, $s = 0, $tz = null)staticthe class you called it onMonth numbers by position, see Hebrew months

The $tz argument takes a DateTimeZone, a time zone name or null. With null the PHP default zone applies, not the zone of any existing Carbon object.

From Carbon to a calendar#

$c = Carbon::create(2026, 3, 21, 12, 0, 0, 'UTC');

echo $c->toJalali();                           // 1405/01/01 12:00:00
echo get_class($c->toJalali());                // RtlyKit\Calendar\Jalali
echo $c->jformat('l j F Y');                   // شنبه 1 فروردین 1405
echo $c->jformat();                            // 1405/01/01 12:00:00
echo $c->toHijri()->format('j F Y', 'en');     // 2 Shawwal 1447
echo $c->toHebrew()->format('j F Y');          // 3 Nisan 5786

The result is a calendar object, not a Carbon object. Use the calendar API from there, for example Persian digits with $c->toJalali()->format('Y/m/d', true). You can keep working with it and go back at any time with toGregorian():

echo Carbon::now('UTC')->setDate(2026, 3, 21)->toJalali()->addMonths(1)->format('Y/m/d');  // 1405/02/01

Calling the calendar's make() with a Carbon instance does the same, because Carbon is a DateTimeInterface:

echo Jalali::make($c)->format('Y/m/d H:i');                                   // 1405/01/01 12:00
echo Jalali::make(Carbon::create(2026, 3, 21, 22, 0, 0, 'UTC'), new DateTimeZone('Asia/Tehran'))->format('Y/m/d H:i');  // 1405/01/02 01:30

From calendar parts to Carbon#

$a = Carbon::createFromJalali(1405, 1, 1, 0, 0, 0, 'UTC');
echo get_class($a), ' ', $a->toDateTimeString();      // Carbon\Carbon 2026-03-21 00:00:00

$b = CarbonImmutable::createFromJalali(1405, 1, 1, 8, 30, 0, 'Asia/Tehran');
echo get_class($b), ' ', $b->toIso8601String();       // Carbon\CarbonImmutable 2026-03-21T08:30:00+03:30

echo Carbon::createFromHijri(1446, 9, 1, 0, 0, 0, 'UTC')->toDateString();   // 2025-03-01
echo Carbon::createFromHebrew(5786, 1, 1, 0, 0, 0, 'UTC')->toDateString();  // 2025-09-23

To use the Tabular Hijri variant, pass it as the last argument: Carbon::createFromHijri(1446, 10, 1, 0, 0, 0, 'UTC', HijriVariant::Tabular).

Errors#

The macros check input through the calendar classes. Invalid or out-of-range input throws RtlyKit\Exceptions\InvalidDateException, not a Carbon InvalidFormatException. An unknown time zone name does the same:

use RtlyKit\Exceptions\RtlyKitThrowable;

$cases = [
    fn () => Carbon::createFromJalali(1404, 12, 30),
    fn () => Carbon::createFromJalali(1404, 1, 1, 0, 0, 0, 'Nowhere/Land'),
    fn () => Carbon::createFromHijri(9999, 1, 1),
    fn () => Carbon::createFromHebrew(1, 1, 1),
];
foreach ($cases as $f) {
    try { $f(); }
    catch (RtlyKitThrowable $e) { echo get_class($e), ': ', $e->getMessage(), "\n"; }
}
// RtlyKit\Exceptions\InvalidDateException: Invalid Jalali date: 1404/12/30
// RtlyKit\Exceptions\InvalidDateException: Unknown timezone: Nowhere/Land
// RtlyKit\Exceptions\InvalidDateException: Invalid Hijri date: 9999/1/1
// RtlyKit\Exceptions\InvalidDateException: Invalid Hebrew date: 1/1/1

The supported ranges are those of the calendars: Jalali -620..9377, Hijri 1..9665, Hebrew 3762..13759.

Good to know#

Good to know. The macros have the same accuracy as the calendar classes. Jalali matches the official calendar for the years 1206 to 1497. Hijri Umm al-Qura month starts match the official KACST calendar for AH 1318 to 1500 (checked 2026-10-08). Day boundaries are at civil midnight.
  • Macros are global per class. If another package defines a macro with the same name (toJalali, toHijri and so on), the one registered last wins. Call the calendar classes directly (Jalali::make($carbon)) when you need to be sure.
  • Time zones travel with the object. toJalali() uses the zone of the Carbon object, so set the zone before converting when the calendar day matters.
  • Weekday numbers differ between calendars. Do not feed getDayOfWeek() of one calendar into another. See Convert and compare dates.