پرش به مطلب اصلی

Hijri — تقویم هجری قمری

یک شیء تاریخ هجری قمری تغییرناپذیر (immutable)، پیاده‌سازی‌شده بر پایهٔ Carbon و افزونهٔ ext-intl — با امکان تبدیل به/از Carbon، Jalali و DateTime.

نصب و راه‌اندازی

ماژول Hijri بخشی از dornica/doravel است و نیازی به نصب جداگانه ندارد.

برای استفاده کافی است در هر بخشی از برنامه کلاس Hijri را وارد کنید:

use Dornica\Foundation\Hijri\Facade\Hijri;
// یا کلاس
use Dornica\Foundation\Hijri\Hijri;

نکته: تابع کمکی سراسری hijri() توسط این پکیج تأمین می‌شود.


متدها

now()

تاریخ هجری قمری لحظهٔ جاری:

$date = Hijri::now();
$date = Hijri::now('Asia/Tehran');
$date = Hijri::now(new DateTimeZone('Asia/Tehran'));

// با تابع کمکی
$date = hijri();

parse()

ساخت از رشتهٔ قمری — فرمت‌های مجاز: Y-m-d یا Y/m/d با یا بدون زمان:

$date = Hijri::parse('1447-09-15');
$date = Hijri::parse('1447/09/15');
$date = Hijri::parse('1447-09-15 14:30:00');

// با تابع کمکی
$date = hijri('1447-09-15');
از ارسال رشتهٔ میلادی به hijri() خودداری کنید

parse() — و در نتیجه hijri('...') — رشته را همیشه قمری فرض می‌کند و هیچ تشخیصی روی میلادی بودن انجام نمی‌دهد. نکتهٔ مهم‌تر اینکه در بیشتر موارد خطایی صادر نمی‌شود و تاریخ نادرست بازگردانده می‌شود:

// ❌ نادرست و بدون خطا — «2026-07-15» عیناً قمری خوانده می‌شود
hijri('2026-07-15')->format('Y/m/d'); // 2026/07/15 (مقدار صحیح: 1448/02/01)

// ❌ بروز خطا قطعی نیست و تنها در برخی حالت‌ها رخ می‌دهد
hijri('2026-07-31');
// InvalidHijriDateException: Invalid Hijri date: 2026/7/31.
// (به دلیل اینکه ماه قمری بیش از 30 روز ندارد، نه به دلیل میلادی بودن سال)

// ✅ شیء تاریخ را مستقیماً ارسال کنید
hijri($user->created_at);
hijri(new DateTime('2026-07-15'));

// ✅ یا در صورتی که تنها اجزای میلادی در دسترس است
Hijri::fromGregorian(2026, 7, 15);

رایج‌ترین موقعیت بروز این خطا زمانی است که تاریخ ابتدا با format() به رشته تبدیل شده و سپس همان رشته به hijri() ارسال می‌شود:

// ❌ تبدیل غیرضروری به رشته و بازگردانی مجدد
$key = $date->format('Y-m-d');
$label = hijri($key)->format('Y/m/d');

// ✅ تبدیل را روی خود شیء تاریخ انجام دهید
$label = hijri($date)->format('Y/m/d');

تابع کمکی hijri() برای Carbon، DateTime، Jalali و timestamp عددی مسیر صحیح را به‌صورت خودکار انتخاب می‌کند؛ تنها رشته است که قمری فرض می‌شود.

fromGregorian()

ساخت از تاریخ میلادی:

$date = Hijri::fromGregorian(2026, 5, 24);
$date = Hijri::fromGregorian(2026, 5, 24, 14, 30, 0, 'Asia/Tehran');

fromCarbon()

ساخت از Carbon:

$date = Hijri::fromCarbon(Carbon::now());
$date = Hijri::fromCarbon(Carbon::create(2026, 5, 24, 12, 0, 0));

// با تابع کمکی
$date = hijri(Carbon::now());

fromJalali()

ساخت از Jalali — تبدیل تاریخ شمسی به قمری:

$date = Hijri::fromJalali(jalali());
$date = Hijri::fromJalali(Jalali::now());

// با تابع کمکی
$date = hijri(jalali());

زنجیرهٔ تبدیل: Jalali (شمسی)Carbon (میلادی)Hijri (قمری)

fromDateTime()

ساخت از DateTime:

$date = Hijri::fromDateTime(new DateTime('2026-05-24'));

// با تابع کمکی
$date = hijri(new DateTime('2026-05-24'));

fromTimestamp()

ساخت از Unix timestamp:

$date = Hijri::fromTimestamp(1748044800);
$date = Hijri::fromTimestamp(1748044800, 'Asia/Tehran');

// با تابع کمکی
$date = hijri(1748044800);

فرمت‌بندی

متدهای آماده

$date = hijri('1447-09-15 08:05:03');

$date->formatDate(); // 1447/09/15
$date->formatDatetime(); // 1447/09/15 08:05:03
$date->formatTime(); // 08:05:03
$date->formatWord(); // 15 رمضان 1447
(string) $date; // 1447/09/15 08:05:03

format()

فرمت‌بندی سفارشی مشابه date() در PHP:

$date->format('Y-m-d'); // 1447-09-15
$date->format('j F Y'); // 15 رمضان 1447
$date->format('d N Y'); // 15 Ramadan 1447
$date->format('Y/m/d H:i'); // 1447/09/15 08:05
$date->format('Y\\-m\\-d'); // 1447-09-15 (کاراکتر گریز با \)

جدول توکن‌ها:

توکنتوضیحمثال
Yسال چهار رقمی1447
yسال دو رقمی47
mماه دو رقمی09
nماه بدون صفر9
dروز دو رقمی05
jروز بدون صفر5
Hساعت ۲۴ ساعته08
Gساعت بدون صفر8
iدقیقه05
sثانیه03
Fنام ماه عربیرمضان
Nنام ماه انگلیسیRamadan
Mنام کوتاه انگلیسیRam
tتعداد روزهای ماه30
UUnix timestamp1748044800

نام ماه‌ها

$date->getMonthName(); // رمضان (پیش‌فرض: عربی)
$date->getMonthName('ar'); // رمضان
$date->getMonthName('en'); // Ramadan

تبدیل

$date = hijri('1447-12-08');

// به Carbon (میلادی)
$carbon = $date->toCarbon();
$carbon->format('Y-m-d'); // 2026-05-24

// به Jalali (شمسی)
$jalali = $date->toJalali();
$jalali->format('Y/m/d'); // 1405/03/03

// به DateTime
$dt = $date->toDateTime();

// به آرایهٔ میلادی
$date->toGregorian();
// ['year' => 2026, 'month' => 5, 'day' => 24]

// Unix timestamp
$date->timestamp(); // int

تغییر تاریخ (Mutation)

تمام متدهای تغییر تغییرناپذیر (immutable) هستند — در هر فراخوانی یک شیء جدید بازگردانده می‌شود و شیء اصلی بدون تغییر باقی می‌ماند.

$base = hijri('1447-06-15 12:00:00');

// روز
$base->addDay(); // 1447/06/16
$base->addDays(10); // 1447/06/25
$base->subDay(); // 1447/06/14
$base->subDays(5); // 1447/06/10

// هفته
$base->addWeek(); // 1447/06/22
$base->addWeeks(2); // 1447/06/29
$base->subWeek(); // 1447/06/08

// ماه
$base->addMonth(); // 1447/07/15
$base->addMonths(8); // 1448/02/15 (سرریز به سال بعد)
$base->subMonth(); // 1447/05/15

// سال
$base->addYear(); // 1448/06/15
$base->addYears(3); // 1450/06/15
$base->subYear(); // 1446/06/15

// ساعت
$base->addHour(); // ساعت 13
$base->addHours(14); // ساعت 2 بامداد روز بعد
$base->subHour(); // ساعت 11

// دقیقه
$base->addMinutes(90); // ساعت 13:30
$base->subMinute(); // 11:59

// شیء base بدون تغییر باقی مانده است
$base->getDay(); // 15

تنظیم‌کننده‌ها

$base->setYear(1448);
$base->setMonth(9);
$base->setDay(1);
$base->setTime(14, 30, 0);

مقایسه

$a = hijri('1447-09-01');
$b = hijri('1447-09-15');

$a->eq($b); // false
$b->gt($a); // true
$a->lt($b); // true
$a->gte($a); // true
$a->lte($b); // true

// بین دو تاریخ
$mid = hijri('1447-09-08');
$mid->between($a, $b); // true

// وضعیت نسبت به زمان حال
hijri('1400-01-01')->isPast(); // true
hijri('1500-01-01')->isFuture(); // true
hijri()->isToday(); // true

// ماه رمضان
hijri('1447-09-15')->isRamadan(); // true

تفاوت

$a->diffDays($b); // 14
$a->diffMonths(hijri('1447-12-01')); // 3
$a->diffYears(hijri('1450-09-01')); // 3

اعتبارسنجی (Validation)

Hijri::isValidDate(1447, 9, 15); // true
Hijri::isValidDate(1447, 13, 1); // false — ماه نامعتبر
Hijri::isValidDate(1447, 1, 31); // false — روز نامعتبر

Hijri::isLeapYear(1447); // true
Hijri::isLeapYear(1446); // false

hijri('1447-09-01')->daysInMonth(); // 29 یا 30

تابع کمکی hijri()

hijri() // لحظهٔ جاری
hijri('1447-09-15') // تجزیهٔ رشتهٔ قمری
hijri('1447-09-15 14:30:00') // با زمان
hijri(Carbon::now()) // از Carbon
hijri(jalali()) // از Jalali (شمسی → قمری)
hijri(new DateTime()) // از DateTime
hijri(1748044800) // از Unix timestamp
hijri('1447-09-15', 'Asia/Tehran') // با تایم‌زون

Facade

use Dornica\Foundation\Hijri\Facade\Hijri;

Hijri::now();
Hijri::parse('1447-09-15');
Hijri::fromGregorian(2026, 5, 24);
Hijri::fromCarbon(Carbon::now());
Hijri::fromJalali(jalali());
Hijri::isLeapYear(1447);
Hijri::isValidDate(1447, 9, 15);

متدهای دسترسی (Getters)

$date = hijri('1447-09-15 08:05:03');

// متد
$date->getYear(); // 1447
$date->getMonth(); // 9
$date->getDay(); // 15
$date->getHour(); // 8
$date->getMinute(); // 5
$date->getSecond(); // 3

// magic property
$date->year; // 1447
$date->month; // 9
$date->day; // 15
$date->hour; // 8
$date->minute; // 5
$date->second; // 3

استثناها (Exceptions)

Exceptionشرایط بروز
InvalidHijriDateExceptionتاریخ نامعتبر، ماه خارج از ۱–۱۲، فرمت ناشناس
ExtensionNotLoadedExceptionافزونهٔ ext-intl نصب نشده است
use Dornica\Foundation\Hijri\Exceptions\InvalidHijriDateException;

try {
$date = hijri('invalid-date');
} catch (InvalidHijriDateException $e) {
// فرمت نامعتبر
}

try {
$date = new Hijri(1447, 13, 1);
} catch (InvalidHijriDateException $e) {
// ماه نامعتبر
}