remls/hijri-date

Laravel helper package for Hijri dates.

Maintainers

Package info

github.com/Remls/hijri-date

pkg:composer/remls/hijri-date

Transparency log

Statistics

Installs: 22 548

Dependents: 0

Suggesters: 0

Stars: 10

Open Issues: 0

v3.0.0 2026-07-22 14:38 UTC

This package is auto-updated.

Last update: 2026-07-22 14:51:43 UTC


README

HijriDate

Laravel helper package for Hijri dates. Supports displaying dates in Arabic, Bengali, Dhivehi, English, Indonesian, Malay and Urdu out of the box, with support for further customizations or adding a language of your choice.

A demo of some of the package's capabilities is available at https://hijri.remls.io.

Today's Hijri date, rendered live by the demo

Warning

This package was built primarily for use in Maldives: the default language is Dhivehi, and the default calendar is a custom mapping of dates specifically for Maldives. (Why?)

However, this can all be customised for your specific needs; read further for how to do so.

Installation

composer require remls/hijri-date

To publish configuration files:

php artisan vendor:publish --provider="Remls\HijriDate\HijriDateServiceProvider" --tag="config"

Creating dates

All of the following methods return an instance of Remls\HijriDate\HijriDate.

use Carbon\Carbon;

new HijriDate();                    // 1st Muharram 1000
new HijriDate(1443, 9, 1);          // 1st Ramadan 1443
HijriDate::parse('1443-09-01');     // 1st Ramadan 1443

// From Gregorian
HijriDate::createFromGregorian();              // Today's date
today_hijri();                                 // Today's date
HijriDate::createFromGregorian('1991-12-01');  // 20th Jumada al-Ula 1412
$input = Carbon::parse('2002-03-04');
HijriDate::createFromGregorian($input);        // 12th Dhul-Hijja 1422

Customizing how dates are converted between Hijri and Gregorian

By default, the package uses an external map between Hijri and Gregorian dates in Maldives to convert between the two. This map is cached and reused for subsequent conversions.

You may customize for how long the map is cached by changing config/hijri.php > conversion.cache_period.

You may manually re-fetch data from the external source by running php artisan hijri:fetch.

The package also comes with an alternative class for converting dates using calculations instead of a map. You may enable it by changing config/hijri.php > conversion.converter to \Remls\HijriDate\Converters\MaldivesEstimateG2HConverter::class.

You may customize how dates are converted by:

  • providing your own map in config/hijri.php > conversion.data_url
  • providing your own custom converter class in config/hijri.php > conversion.converter
    • The class must implement \Remls\HijriDate\Converters\Contracts\GregorianToHijriConverter.
  • binding your own implementation of GregorianToHijriConverter in the service container
    • This takes precedence over config/hijri.php > conversion.converter
    • Example:
// App/Providers/AppServiceProvider.php

use Remls\HijriDate\Converters\Contracts\GregorianToHijriConverter;

public function register(): void
{
    $this->app->singleton(GregorianToHijriConverter::class, fn () => new YourCustomConverter());
}

Available methods

Conversions

// Gregorian to Hijri
HijriDate::createFromGregorian('1991-12-01'); // returns HijriDate

// Hijri to Gregorian
$date = new HijriDate(1443, 9, 1);
$date->getGregorianDate();                    // returns Carbon

By default, conversions are date-only: any time component on the input is ignored, and input Carbon instances are never modified. getGregorianDate() returns a copy of the original input (for dates created with createFromGregorian()), or a date at midnight in Maldives time (UTC+5), derived on first call.

You may customise how the conversion works, as detailed here.

Calculations

$date = new HijriDate(1443, 9, 1);   // 1st Ramadan 1443
$date->addDays(1);                   // 2nd Ramadan 1443
$date->subDays(3);                   // 28th Sha'ban 1443
$date2 = new HijriDate(1443, 8, 20); // 20th Sha'ban 1443
$date->diffInDays($date2);           // 8

Comparisons

You may compare two HijriDate objects $a and $b using the following methods:

Method Description
$a->compareWith($b) Returns -1 if $a < $b.
Returns 0 if $a == $b.
Returns 1 if $a > $b.
$a->equalTo($b) Returns true if $a == $b.
$a->greaterThan($b) Returns true if $a > $b (a is after b).
$a->lessThan($b) Returns true if $a < $b (a is before b).
$a->greaterThanOrEqualTo($b) Returns true if $a >= $b (a is after or equal to b).
$a->lessThanOrEqualTo($b) Returns true if $a <= $b (a is before or equal to b).

Formatting

Each HijriDate object will have a set locale when it is created. This locale will be used for formatting.

The locale is 'dv' by default, but you may customize it by:

  • passing locale in constructor (eg: new HijriDate(1443, 9, 1, 'en'))
  • changing locale after creation (eg: $date->setLocale('en'))
  • changing default_locale in configuration, so all HijriDate objects are created using that default locale

The following options are supported with $date->format():

Option Description Example
d Day of month (with leading zero) 01 ... 30
D Weekday (short) Sun ... Sat
j Day of month (without leading zero) 1 ... 30
l
(lowercase L)
Weekday Sunday ... Saturday
F Month Muharram ... Dhul-Hijja
m Month (number, with leading zero) 01 ... 12
M Month (short) Mhr ... DhH
n Month (number, without leading zero) 1 ... 12
Y Year 1000 ... 1999
y Year (final two digits) 00 ... 99
$date = new HijriDate(1443, 9, 1);  // 1st Ramadan 1443
$date->format("F");                 // "ރަމަޟާން" (using default locale 'dv')

$date->setLocale('ar');
$date->format("F Y");               // "رمضان 1443"
// Use numerals from locale
$date->format("F Y", true);         // "رمضان ١٤٤٣"

Casting

The field to be cast must be a string field on database.

// App/Models/YourModel.php

class YourModel
{
    ...

    protected $casts = [
        ...
        'your_hijri_date_field' => HijriDate::class,
    ];
}

This will automatically store data as Y-m-d string in database, and cast to Remls\HijriDate\HijriDate when accessing.

Validation

Any string that passes the following conditions is considered a valid Hijri date:

  • in the format Y-m-d
  • year between 1000 and 1999 (This can be changed in config.)
  • month between 1 and 12
  • day between 1 and 30
use Remls\HijriDate\Rules\ValidHijriDate;

...
request()->validate([
    'your_hijri_date_field' => ['required', new ValidHijriDate],
]);

Note that validation error messages will use app's locale (unlike formatting).

Localization

Publish translation files by using:

php artisan vendor:publish --provider="Remls\HijriDate\HijriDateServiceProvider" --tag="lang"

You may then customize strings as needed.

Adding a language

To add support for another language:

  1. Publish the configuration file. The file will be copied to config/hijri.php.
  2. Publish the translation files. The files will be copied to lang/vendor/hijri.
  3. Copy one of the existing translation folders, and rename it with the language code of your choice. Eg: lang/vendor/hijri/es
  4. Change strings to their respective translations.
  5. Add the language code to supported_locales in config/hijri.php.
  6. (Optional) Change default_locale in config/hijri.php to the new language code.

Migrating from v2 to v3

This version mainly addresses stale code and bug fixes, with little to no change in functionality for the vast majority of cases.

Minimum supported Laravel version has been bumped from v8 to v10.

⚠️ denotes new behaviour that could potentially be a breaking change.

Improvements

  • ValidHijriDate now implements Illuminate\Contracts\Validation\ValidationRule instead of the deprecated Rule.
    • Normal usage (new ValidHijriDate in a rules array) is unaffected.
    • ⚠️ If you called passes() or message() on the rule directly, or extended the class, update to the new validate() method.
  • The map is now fetched with Laravel's HTTP client, with timeouts and a retry.
    • ⚠️ Network failures throw Illuminate\Http\Client\ConnectionException or RequestException instead of RuntimeException. Invalid map data still throws RuntimeException.
    • allow_url_fopen is no longer required.
    • Fetched data is validated before use, and the last successfully fetched map is kept indefinitely and served as a fallback if a refetch fails.
  • The converter is resolved from the service container as a singleton, instead of being constructed for every conversion.
    • You can now register your conversion class in AppServiceProvider instead of changing conversion.converter in config.
    • ⚠️ Changing conversion.converter in config at runtime after the first conversion now has no effect.

Bug fixes

  • conversion.cache_period is now set to 6 hours by default. If you have published config/hijri.php, update conversion.cache_period to 60 * 60 * 6 (optional, leaving as the previous default just means the cache expires more quickly).
  • ⚠️ Carbon instances passed into the package are never modified anymore.
    • Previously, an instance passed to createFromGregorian() was silently mutated by the default converter class. It now stays exactly as you created it, and getGregorianDate() returns a copy of it as provided.
  • Negative amounts passed to addDays() / subDays() now stay on the calculation path selected by $useGregorian. Previously they always used the Gregorian path.
  • ⚠️ Out-of-range results from addDays() / subDays() with $useGregorian = false now throw InvalidArgumentException, consistent with the rest of the package, instead of OutOfRangeException.
  • isParsable() now checks the year against the configured range, so it no longer returns true for strings that parse() would then reject.

Migrating from v1 to v2

The package no longer uses estimates when converting from Hijri to Gregorian by default.

  • New keys have been added to config/hijri.php. You may need to update your configuration file.
  • The function getEstimateFromGregorian has been REMOVED in favour of createFromGregorian.
    • To maintain the same behaviour as before:
      1. Change config/hijri.php > conversion.converter to \Remls\HijriDate\Converters\MaldivesEstimateG2HConverter::class.
      2. Change all calls from getEstimateFromGregorian to createFromGregorian.
  • The function isEstimate has been REMOVED. There is no more need to check if the date was made from an estimate, as you can now always just get the corresponding Gregorian date with a call to getGregorianDate, regardless of how it was created.
  • The function getEstimatedFrom has been REMOVED in favour of getGregorianDate.
  • The function resetEstimation has been REMOVED in favour of resetGregorianDate.
  • The functions addDays and subDays will now use the underlying Gregorian date by default for calculations.
    • To maintain the same behaviour as before, pass false to $useGregorian parameter of these functions.