در این صفحه
این کلاس چه میکند#
کلاس RtlyKit\Calendar\Hijri یک تاریخ و زمان در تقویم هجری قمری است و تغییر نمیکند. مثل دو کلاس دیگر، یک DateTimeImmutable زیرش است و قرارداد مشترک CalendarDate را دارد (تبدیل و مقایسهٔ تاریخها). چیزی که هجری را متفاوت میکند گونه (variant) است، یعنی قاعدهای که طول ماهها را میگوید. هر شیء گونهٔ خودش را دارد و هر متدی که تاریخ را عوض کند آن را نگه میدارد.
مثالها با این چند خط شروع میشوند (نصب):
<?php
require 'vendor/autoload.php';
use RtlyKit\Calendar\Hijri;
use RtlyKit\Calendar\HijriVariant;
use RtlyKit\Calendar\Jalali;
use RtlyKit\Exceptions\InvalidDateException;
use function RtlyKit\hdate; // تابع کمکی فضاینامدار، همان Hijri::make()
$utc = new DateTimeZone('UTC');
گونهها: امالقری و Tabular#
| گونه | طول ماهها از کجا میآید |
|---|---|
HijriVariant::UmmAlQura (پیشفرض) | جدول طول ماهها که داخل بسته است و سالهای ۱۳۰۰ تا ۱۵۰۰ هجری قمری را میپوشاند (از 1882-11-12 تا 2077-11-16). بیرون از آن، قاعدهٔ Tabular جای آن را میگیرد. |
HijriVariant::Tabular | تقویم مدنی حسابی: چرخهٔ ۳۰ ساله با ۱۱ سال کبیسه. برای هر سال جواب قطعی میدهد، ولی ممکن است یک یا دو روز با امالقری فرق داشته باشد. |
$uq = Hijri::create(1446, 10, 1, 0, 0, 0, $utc); // امالقری
$tab = Hijri::create(1446, 10, 1, 0, 0, 0, $utc, HijriVariant::Tabular);
echo $uq->toGregorian()->format('Y-m-d'); // 2025-03-30
echo $tab->toGregorian()->format('Y-m-d'); // 2025-03-31
echo $uq->getVariant()->name; // UmmAlQura
دو نقطهٔ مرجع دیگر در امالقری: اول رمضان ۱۴۴۶ برابر 2025-03-01 و اول محرم ۱۴۴۷ برابر 2025-06-26 است.
جدول امالقری از دادههای تقویم islamic-umalqura در ICU/CLDR ساخته شده است. آغاز ماهها را برای سالهای ۱۳۱۸ تا ۱۵۰۰ هجری قمری با تقویم رسمی KACST مقایسه کردهایم (در 2026-10-08، ۲۱۹۶ ماه، بدون هیچ اختلاف).
پوشش جدول#
بیرون از ۱۳۰۰ تا ۱۵۰۰، create() و make() خطا نمیدهند. آنها با قاعدهٔ Tabular ادامه میدهند. با Hijri::hasUmmAlQuraData($year) و $date->usesUmmAlQuraTable() میفهمید کدام قاعده به کار رفته. بازهٔ سالهایی که با تقویم رسمی KACST مقایسه شدهاند را هم با Hijri::ummAlQuraVerifiedRange() و Hijri::isUmmAlQuraVerified($year) میگیرید.
echo Hijri::hasUmmAlQuraData(1299) ? 1 : 0; // 0
echo Hijri::hasUmmAlQuraData(1300) ? 1 : 0; // 1
echo Hijri::hasUmmAlQuraData(1500) ? 1 : 0; // 1
echo Hijri::hasUmmAlQuraData(1501) ? 1 : 0; // 0
echo json_encode(Hijri::ummAlQuraVerifiedRange()); // [1318,1500]
var_dump(Hijri::isUmmAlQuraVerified(1446)); // bool(true)
var_dump(Hijri::isUmmAlQuraVerified(1310)); // bool(false)
$o = Hijri::create(1600, 1, 1, 0, 0, 0, $utc);
echo $o->toGregorian()->format('Y-m-d'); // 2173-12-06
var_dump($o->usesUmmAlQuraTable()); // bool(false) (ادامه با قاعدهٔ Tabular)
var_dump(Hijri::create(1446, 9, 1, 0, 0, 0, $utc)->usesUmmAlQuraTable()); // bool(true)
ساخت تاریخ#
create()#
متد Hijri::create($year, $month, $day, $hour = 0, $minute = 0, $second = 0, $timezone = null, $variant = HijriVariant::UmmAlQura) طول واقعی ماه را در گونهٔ انتخابشده چک میکند.
$h = Hijri::create(1446, 9, 1, 0, 0, 0, $utc);
echo $h; // 1446/09/01 00:00:00
echo $h->toGregorian()->format('Y-m-d'); // 2025-03-01
var_dump($h->usesUmmAlQuraTable()); // bool(true)
make() و تابع کمکی#
متد Hijri::make($time = null, $timezone = null, $variant = HijriVariant::UmmAlQura) این ورودیها را قبول میکند: DateTimeInterface، هر CalendarDate، زمان یونیکس (int)، رشته یا null (همین الان). تابع hdate($time, $timezone) آن را با گونهٔ پیشفرض صدا میزند. Hijri::now() و Hijri::today() آرگومانهای ($timezone, $variant) دارند.
echo Hijri::make('2025-03-01', $utc); // 1446/09/01 00:00:00
echo hdate('2025-03-01')->format('j F Y', 'en'); // 1 Ramadan 1446
echo Hijri::make(Jalali::create(1404, 1, 1, 0, 0, 0, $utc)); // 1446/09/21 00:00:00
echo Hijri::make('2025-03-31', $utc)->format('Y/m/d'); // 1446/10/02
echo Hijri::make('2025-03-31', $utc, HijriVariant::Tabular)->format('Y/m/d'); // 1446/10/01
رشته چطور خوانده میشود#
- رقمهای فارسی و عربی به انگلیسی تبدیل میشوند و فاصلههای دو سر رشته حذف میشود. رشتهٔ خالی
InvalidDateExceptionمیدهد. - رشتهای با شکل
Y/m/dیاY-m-d(سال ۳ یا ۴ رقم، باH:i[:s]اختیاری) که سالش کمتر از ۱۷۰۰ باشد، تاریخ هجری در گونهٔ انتخابشده است. - هر چیز دیگر، از جمله سالهای ۱۷۰۰ به بعد، میلادی یا متن آزاد خوانده میشود.
echo Hijri::make('1446/09/01', $utc); // 1446/09/01 00:00:00 (هجری)
echo Hijri::make('1700/01/01', $utc); // 1111/07/10 00:00:00 (میلادیِ 1700-01-01)
try { Hijri::make('1446/02/31'); }
catch (InvalidDateException $e) { echo $e->getMessage(); } // Invalid Hijri date: 1446/2/31
Hijri::make($hijriInstance) همان شیء را بدون تغییر برمیگرداند و $variant را نادیده میگیرد. اگر همان تاریخ را در گونهٔ دیگر میخواهید، از لحظه بسازید: Hijri::make($h->toGregorian(), null, HijriVariant::Tabular).بازهٔ سال و خطاها#
سال هجری از Hijri::MIN_YEAR = 1 تا Hijri::MAX_YEAR = 9665 پشتیبانی میشود. اولین روز 1/1/1 (برابر 0622-07-19) و آخرین روز، آخر ماه ۱۲ سال ۹۶۶۵ (برابر 9999-10-01) است. هر چیز بیرون از این بازه، چه از create() و make() و زمان یونیکس و add*() و sub*() و چه از تبدیلها، فقط InvalidDateException میدهد. TypeError یا ValueError بیرون نمیآید.
foreach ([[0, 1, 1], [9666, 1, 1], [1446, 1, 31], [1446, 13, 1]] as [$y, $m, $d]) {
try { Hijri::create($y, $m, $d); }
catch (InvalidDateException $e) { echo $e->getMessage(), "\n"; }
}
// Invalid Hijri date: 0/1/1
// Invalid Hijri date: 9666/1/1
// Invalid Hijri date: 1446/1/31
// Invalid Hijri date: 1446/13/1
try { Hijri::make(new DateTimeImmutable('0600-01-01', $utc)); }
catch (InvalidDateException $e) { echo $e->getMessage(); }
// Date out of the supported Hijri range (1..9665): -22
try { Hijri::create(9665, 1, 1)->addYears(1); }
catch (InvalidDateException $e) { echo $e->getMessage(); }
// Hijri year out of the supported range: 9666
خواندن مقدارها#
گیرندهها همانهایی هستند که قرارداد مشترک میگوید: getYear()، getMonth()، getDay()، getHour()، getMinute()، getSecond()، getTimestamp()، getTimezone()، toGregorian()، toDateString() (به شکل Y/m/d) و toDateTimeString(). هجری دو متد دیگر هم دارد: getVariant() و usesUmmAlQuraTable().
شمارهٔ روز هفته#
getDayOfWeek() و توکن w از یکشنبه شروع میکنند: ۰ = یکشنبه تا ۶ = شنبه (مثل PHP). در جلالی شمارش از شنبه شروع میشود. توکن N شمارهٔ ISO است: ۱ = دوشنبه تا ۷ = یکشنبه.
$h = Hijri::create(1446, 9, 1, 0, 0, 0, $utc); // روز شنبه
echo $h->getDayOfWeek(); // 6
foreach (range(0, 6) as $i) { echo $h->addDays($i)->getDayOfWeek(); } // 6012345
قالببندی#
format($pattern = 'Y/m/d H:i:s', $locale = 'ar', $digits = 'latin'). مقدار $locale یکی از ar، fa یا en است (مقدار ناشناخته مثل en رفتار میکند). این مقدار نام ماه و روز و نشانگر قبل و بعد از ظهر را تعیین میکند. $digits یکی از latin، persian یا arabic است. بکاسلش نویسهٔ بعدی را همانطور که هست مینویسد (بکاسلشِ آخر الگو نادیده گرفته میشود). الگوی بلندتر از Hijri::MAX_FORMAT_LENGTH (۲۵۶ بایت) InvalidDateException با کد input_too_long میدهد. زبان پیشفرض عربی است.
echo $h->format('l j F Y'); // السبت 1 رمضان 1446
echo $h->format('l j F Y', 'fa'); // شنبه 1 رمضان 1446
echo $h->format('l j F Y', 'en'); // Saturday 1 Ramadan 1446
echo $h->format('Y/m/d', 'ar', 'arabic'); // ١٤٤٦/٠٩/٠١
echo $h->format('j F Y', 'fa', 'persian'); // ۱ رمضان ۱۴۴۶
echo $h->format('t L z N'); // 29 0 237 6
$pm = Hijri::create(1446, 9, 1, 15, 0, 0, $utc);
echo $pm->format('g:i a', 'ar'); // 3:00 م
echo $pm->format('g:i A', 'en'); // 3:00 PM
| توکن | معنی |
|---|---|
Y y m n d j | سال (۴ و ۲ رقمی)، ماه و روز، با صفر ابتدایی و بدون آن |
H G h g i s | ساعت ۲۴ و ۱۲ ساعته، دقیقه، ثانیه |
F M | نام ماه به زبان انتخابشده |
l | نام روز هفته به زبان انتخابشده |
w N | شمارهٔ روز هفته با یکشنبه = ۰. و شمارهٔ ISO با دوشنبه = ۱ |
z t L | شمارهٔ روز در سال از صفر. تعداد روزهای ماه. و ۱ برای سال کبیسه (۳۵۵ روز) |
a A | قبل و بعد از ظهر، بسته به زبان (ص/م، ق.ظ/ب.ظ، am/pm) |
S W | همیشه خالی. و هفتهٔ ISO لحظهٔ میلادی |
c r | Y-m-d\TH:i:sP با تاریخ هجری. و RFC 2822 لحظهٔ میلادی |
U e T P p O Z I u v | از لحظهٔ زیرین گرفته میشوند |
متد استاتیک Hijri::monthName($month, $locale = 'ar') نام ماه را میدهد. برای ماه بیرون از ۱ تا ۱۲ InvalidDateException میدهد:
echo Hijri::monthName(9), ' | ', Hijri::monthName(9, 'fa'), ' | ', Hijri::monthName(9, 'en');
// رمضان | رمضان | Ramadan
// Hijri::monthName(13) throws: Invalid Hijri month: 13
جابهجایی و برش#
متدها همانهایی هستند که در جلالی دیدید: addDays/Hours/Minutes/Seconds، addMonths، addYears، نسخههای sub* و startOf/endOf برای روز و ماه و سال. ماه، ماه هجریِ همان گونه است. اگر ماه مقصد کوتاهتر باشد، روز کم میشود تا در ماه جا شود.
echo Hijri::create(1446, 4, 30, 0, 0, 0, $utc)->addMonths(1); // 1446/05/29 00:00:00 (روز کم شد)
echo $h->addYears(1); // 1447/09/01 00:00:00
echo $h->subMonths(9); // 1445/12/01 00:00:00
echo $h->addDays(29); // 1446/10/01 00:00:00
echo $h->endOfMonth(); // 1446/09/29 23:59:59
echo $h->endOfYear(); // 1446/12/29 23:59:59
echo $h->startOfYear(); // 1446/01/01 00:00:00
echo $tab->addMonths(1)->getVariant()->name; // Tabular (گونه میماند)
رفتن به بیرون از سال ۱ یا ۹۶۶۵، یا جابهجایی با مقدار غیرمنطقی، InvalidDateException میدهد.
مقایسه و اختلاف#
مقایسهها (eq ne gt gte lt lte equals isBefore isAfter between isPast isFuture isToday) روی لحظهها انجام میشوند و هر تقویمی را قبول میکنند. diffInDays() روز کامل میشمارد. diffInMonths() و diffInYears() ماه و سال کامل هجری را میشمارند، در گونهٔ همین شیء (تاریخ دیگر اول به همان گونه برده میشود). پارامتر $absolute مثل جلالی کار میکند.
echo Hijri::create(1446, 1, 1, 0, 0, 0, $utc)->diffInMonths(Hijri::create(1447, 3, 1, 0, 0, 0, $utc)); // 14
echo Hijri::create(1440, 1, 1, 0, 0, 0, $utc)->diffInYears(Hijri::create(1446, 1, 1, 0, 0, 0, $utc)); // 6
echo $h->diffInDays(Hijri::create(1446, 10, 1, 0, 0, 0, $utc)); // 29
محاسبههای تقویمی#
| متد | خروجی |
|---|---|
Hijri::isValid($y, $m, $d, $variant) | bool. برای مقدار بیرون از بازه false |
Hijri::daysInMonth($y, $m, $variant) | ۲۹ یا ۳۰. برای ماه بیرون از ۱ تا ۱۲ InvalidDateException |
Hijri::daysInYear($y, $variant) | جمع طول ۱۲ ماه (در عمل ۳۵۴ یا ۳۵۵) |
Hijri::isLeapYear($y, $variant) | true وقتی سال ۳۵۵ روز دارد |
Hijri::hasUmmAlQuraData($y) | true برای ۱۳۰۰ تا ۱۵۰۰ هجری قمری |
Hijri::ummAlQuraVerifiedRange() | [1318, 1500]، سالهایی که با تقویم رسمی KACST مقایسه شدهاند |
Hijri::isUmmAlQuraVerified($y) | true برای سالی که در این بازه باشد |
Hijri::gregorianToHijri($gy, $gm, $gd, $variant) | [سال, ماه, روز] |
Hijri::hijriToGregorian($hy, $hm, $hd, $variant) | [سال, ماه, روز] |
echo implode(',', array_map(fn ($m) => Hijri::daysInMonth(1446, $m), range(1, 12)));
// 29,30,30,30,29,30,30,29,29,30,29,29
echo Hijri::daysInYear(1446); // 354
echo Hijri::daysInMonth(1446, 12, HijriVariant::Tabular); // 29
echo json_encode(Hijri::gregorianToHijri(2025, 3, 1)); // [1446,9,1]
echo json_encode(Hijri::hijriToGregorian(1446, 9, 1)); // [2025,3,1]
نکتههای کاربردی#
- گونه را یک بار برای کل برنامه انتخاب کنید و همهجا همان را بدهید. دو گونه را در یک مقایسه قاطی نکنید، مگر برای دیدن اختلافهای یکروزه.
- برای سالهای ۱۳۰۰ تا ۱۵۰۰ هجری قمری مرجع کتابخانه جدول امالقری است. بیرون از آن، نتیجه با قاعدهٔ حسابی ادامه پیدا میکند.
- تبدیل به تقویمهای دیگر در تبدیل و مقایسهٔ تاریخها آمده است. متدهای Carbon (
toHijri()وcreateFromHijri()) در ماکروهای Carbon هستند.