في هذه الصفحة
ما الذي تفعله الفئة#
RtlyKit\Calendar\Jalali هي قيمة تاريخ ووقت لا تتغير بعد إنشائها، في التقويم الفارسي (الشمسي الهجري). تحتفظ الفئة في داخلها بلحظة واحدة من النوع DateTimeImmutable؛ ولذلك لها منطقة زمنية وطابع زمني (Unix timestamp) ونظير ميلادي دقيق، وتُحسب السنة والشهر واليوم الجلاليّة من هذه اللحظة. كل دالة تعديل تُرجع كائنًا جديدًا، ويبقى الأصل كما هو. الفئة final وتنفّذ العقد المشترك CalendarDate الموصوف في التحويل والمقارنة بين التواريخ.
المتطلبات. ثبّت الحزمة أولًا (التثبيت). تفترض كل الأمثلة الترويسة التالية:
<?php
require 'vendor/autoload.php';
use RtlyKit\Calendar\Jalali;
use RtlyKit\Exceptions\InvalidDateException;
use function RtlyKit\jdate; // مساعد ضمن فضاء الأسماء، مكافئ لـ Jalali::make()
إنشاء التواريخ#
من المكوّنات: create()#
تبني create($year, $month, $day, $hour = 0, $minute = 0, $second = 0, $timezone = null) تاريخًا من مكوّنات جلالية. يُتحقَّق من التاريخ بحسب الطول الفعلي للشهر في تلك السنة، فيُرفض 1404/12/30 (السنة 1404 ليست كبيسة) ويُقبل 1403/12/30. وإذا لم تُحدَّد منطقة زمنية تُستخدم المنطقة الزمنية الافتراضية في PHP (date_default_timezone_get()).
$tehran = new DateTimeZone('Asia/Tehran');
$d = Jalali::create(1404, 7, 15, 14, 30, 5, $tehran);
echo $d; // 1404/07/15 14:30:05
echo $d->getTimestamp(); // 1759834805
echo $d->toGregorian()->format('c'); // 2025-10-07T14:30:05+03:30
من أي قيمة: make()#
تقبل Jalali::make($time = null, $timezone = null) كائنًا من DateTimeInterface، أو CalendarDate آخر (Hijri أو Hebrew أو Jalali)، أو طابعًا زمنيًا Unix من النوع int، أو سلسلة نصية، أو null (الآن). والمساعد jdate() اسم مستعار خفيف لها ضمن فضاء الأسماء.
$utc = new DateTimeZone('UTC');
echo Jalali::make(new DateTimeImmutable('2026-03-21 12:00', $utc)); // 1405/01/01 12:00:00
echo Jalali::make(0, $utc); // 1348/10/11 00:00:00
echo jdate('2026-03-21')->format('l j F Y'); // شنبه 1 فروردین 1405
echo Jalali::now(); // اللحظة الحالية في المنطقة الزمنية الافتراضية
echo Jalali::today(); // اليوم عند 00:00:00
دلالات السلاسل النصية في make()#
تُعالَج السلاسل النصية بهذا الترتيب:
- تُحوَّل الأرقام الفارسية والعربية إلى أرقام إنجليزية، وتُقصّ المسافات من الطرفين. السلسلة الفارغة أو المكوّنة من مسافات فقط تُطلق
InvalidDateException. - إذا كانت السلسلة بالصيغة
Y/m/dأوY-m-dبسنة من 3 أو 4 أرقام، يليها اختياريًاH:iأوH:i:s(يفصلهما مسافة أوT)، وكانت السنة أقل من 1700، فإنها تُقرأ تاريخًا جلاليًا. - كل سلسلة أخرى، بما فيها أي سنة 1700 أو أكبر، تُمرَّر إلى
DateTimeImmutableوتُقرأ تاريخًا ميلاديًا أو نصًا حرًّا ('2025-10-07'و'tomorrow'و'10 October 2025').
echo Jalali::make('1403/12/30'); // 1403/12/30 00:00:00
echo Jalali::make('۱۴۰۴/۰۷/۱۵'); // 1404/07/15 00:00:00 (أرقام فارسية)
echo Jalali::make('1404-07-15 08:05'); // 1404/07/15 08:05:00
echo Jalali::make('2025-10-07')->format('Y/m/d'); // 1404/07/15 (نص ميلادي)
echo Jalali::make('1700/01/01')->format('Y/m/d'); // 1078/10/12 (السنة 1700 فما فوق تُقرأ ميلادية)
'0001-01-01' أو '1500-06-01' على أنها تاريخ جلالي. إذا أردت تاريخًا ميلاديًا قبل السنة 1700، فأنشئ كائن DateTimeImmutable ومرّره إلى make().المدخلات غير الصالحة تُطلق دائمًا InvalidDateException:
foreach (['1404/12/30', '1404/13/01', '1404/07/15 25:00', '', 'garbage'] as $s) {
try {
Jalali::make($s);
} catch (InvalidDateException $e) {
echo "'$s': ", $e->getMessage(), "\n";
}
}
// '1404/12/30': Invalid Jalali date: 1404/12/30
// '1404/13/01': Invalid Jalali date: 1404/13/1
// '1404/07/15 25:00': Invalid time: 25:0:0
// '': Unable to parse an empty date string.
// 'garbage': Unable to parse date: garbage
بصيغة محددة: createFromFormat()#
تحلّل Jalali::createFromFormat($format, $time, $timezone = null) النص بصياغة رموز DateTime::createFromFormat في PHP (ويجوز أن تكون الأرقام فارسية)، لكنها تفسّر السنة والشهر واليوم قيمًا جلالية. لا تنطبق القيود الخاصة بالتقويم الميلادي مثل «فبراير فيه 29 يومًا على الأكثر»؛ فالتقويم الجلالي هو الذي يتحقق من النتيجة.
echo Jalali::createFromFormat('Y/m/d H:i', '1404/07/15 08:30', $tehran); // 1404/07/15 08:30:00
echo Jalali::createFromFormat('d-m-Y', '31-06-1404'); // 1404/06/31 00:00:00
try {
Jalali::createFromFormat('Y/m/d', 'abc');
} catch (InvalidDateException $e) {
echo $e->getMessage(); // Unable to parse 'abc' with format 'Y/m/d'
}
النطاق المدعوم والأخطاء#
السنوات الجلالية المدعومة من Jalali::MIN_YEAR = -620 إلى Jalali::MAX_YEAR = 9377. وهذا هو النطاق الذي يقابل السنوات الميلادية من 1 إلى 9999 بالضبط: أول يوم مدعوم هو -620/01/01 (0001-03-21) وآخر يوم هو 9377/12/30 (9999-03-20). تتحقق كل نقطة دخول من النطاق ولا تُطلق إلا InvalidDateException (ولا تُطلق أبدًا TypeError أو ValueError أو استثناءات DateMalformed*)، ويشمل ذلك create() وmake() وcreateFromFormat() والطوابع الزمنية الصحيحة وadd*()/sub*() ودوال التحويل الساكنة.
foreach ([[-621, 1, 1], [9378, 1, 1], [1404, 12, 30], [1404, 0, 1], [1404, 7, 31]] as [$y, $m, $d]) {
try {
Jalali::create($y, $m, $d);
} catch (InvalidDateException $e) {
echo $e->getMessage(), "\n";
}
}
// Invalid Jalali date: -621/1/1
// Invalid Jalali date: 9378/1/1
// Invalid Jalali date: 1404/12/30
// Invalid Jalali date: 1404/0/1
// Invalid Jalali date: 1404/7/31
ويُتحقَّق من مكوّنات الوقت أيضًا: Jalali::create(1404, 1, 1, 24, 0, 0) تُطلق Invalid time: 24:0:0. والطابع الزمني الصحيح الواقع خارج السنوات الميلادية 1 إلى 9999 يُطلق Timestamp out of the supported range: ....
اِلتقط InvalidDateException لمعالجة مشكلات التقويم وحدها، أو الفئة الأساسية RtlyKit\Exceptions\RtlyKitException (وكذلك الواجهة العلامة RtlyKitThrowable) لمعالجة كل أخطاء المكتبة. راجع معالجة الأخطاء.
قراءة القيم#
| الدالة | ما تُرجعه |
|---|---|
getYear(), getMonth(), getDay() | السنة الجلالية، والشهر (من 1 إلى 12)، واليوم (من 1 إلى 31) |
getHour(), getMinute(), getSecond() | الوقت من اليوم في المنطقة الزمنية للنسخة |
getDayOfWeek() | من 0 = السبت (Shanbe) إلى 6 = الجمعة (Jomeh) |
monthName() | اسم الشهر بالفارسية، مثل مهر |
getTimestamp(), getTimezone() | الطابع الزمني Unix وDateTimeZone |
toGregorian() | الكائن DateTimeImmutable الأساسي |
toDateString(), toDateTimeString(), __toString() | Y/m/d وY/m/d H:i:s وY/m/d H:i:s |
ترقيم أيام الأسبوع#
يبدأ الأسبوع الجلالي يوم السبت: تُرجع getDayOfWeek() ورمز التنسيق w القيمة 0 للسبت و6 للجمعة؛ أما الرمز N فمن 1 (السبت) إلى 7 (الجمعة). أما Hijri وHebrew فمختلفان: إذ يبدآن من الأحد (0 = الأحد) مثل الرمز w في PHP نفسه. لا تشارك أرقام أيام الأسبوع بين التقويمات؛ قارن اللحظات بدلًا من ذلك.
$d = Jalali::create(1404, 7, 15); // يوم ثلاثاء
foreach (range(0, 6) as $i) {
$x = $d->addDays($i);
echo $x->getDayOfWeek(), ':', $x->format('l'), ' ';
}
// 3:سهشنبه 4:چهارشنبه 5:پنجشنبه 6:جمعه 0:شنبه 1:یکشنبه 2:دوشنبه
التنسيق#
تقيّم format($pattern = 'Y/m/d H:i:s', $persianDigits = false) رموز بأسلوب date() في PHP ضمن التقويم الجلالي. مرّر true وسيطًا ثانيًا لتحويل كل الأرقام إلى الفارسية. تُبطل الشرطة المائلة العكسية الخاصية الرمزية للمحرف الذي يليها. والمحارف التي ليست رموزًا تُنسخ كما هي. النمط الأطول من Jalali::MAX_FORMAT_LENGTH (256 بايتًا) يُطلق InvalidDateException.
$d = Jalali::create(1404, 7, 15, 14, 30, 5, $tehran);
echo $d->format('l j F Y'); // سهشنبه 15 مهر 1404
echo $d->format('Y/m/d', true); // ۱۴۰۴/۰۷/۱۵
echo $d->format('c'); // 1404-07-15T14:30:05+03:30
echo $d->format('h:i A'); // 02:30 بعد از ظهر
echo $d->format('g:i a'); // 2:30 ب.ظ
echo $d->format('\Y: Y, \d: d'); // Y: 1404, d: 15
echo $d->format('t L z W N D'); // 30 0 200 41 4 س
| الرمز | المعنى |
|---|---|
Y y | السنة، من 4 أرقام ومن رقمين |
m n | الشهر مع صفر بادئ وبدونه |
F M | اسم الشهر بالفارسية (M مطابق لـ F: فأسماء الأشهر الفارسية لا اختصار لها) |
d j | اليوم مع صفر بادئ وبدونه |
l D | اسم يوم الأسبوع؛ وD هي الصيغة من حرف واحد (ش ی د س چ پ ج) |
w N | رقم يوم الأسبوع: من 0 إلى 6 ومن 1 إلى 7، ويبدأ بالسبت |
z | رقم اليوم في السنة الجلالية بدءًا من الصفر |
t L | عدد أيام الشهر؛ و1 إذا كانت السنة كبيسة |
H G h g | نظام 24 ساعة ونظام 12 ساعة، مع صفر بادئ وبدونه |
i s | الدقائق والثواني |
a A | ق.ظ/ب.ظ وقبل از ظهر/بعد از ظهر |
S | فارغ دائمًا (الفارسية لا لاحقة ترتيبية فيها) |
W | رقم الأسبوع بحسب ISO-8601 للحظة الميلادية الأساسية |
c | Y-m-d\TH:i:sP مع التاريخ الجلالي |
r | سلسلة RFC 2822 للحظة الميلادية (يشترط RFC الأسماء الإنجليزية الميلادية) |
U e T P p O Z I u v | تُفوَّض إلى اللحظة الأساسية (الطابع الزمني، والمنطقة، والإزاحة، وأجزاء الثانية الدقيقة، ...) |
الإزاحة والضبط#
كل دالة تعديل تُرجع نسخة جديدة؛ وتبقى المنطقة الزمنية محفوظة.
| الدالة | السلوك |
|---|---|
addDays() subDays() | أيام كاملة على اللحظة الأساسية |
addHours() addMinutes() addSeconds() وصيغ sub* | زمن منقضٍ فعلي |
addMonths() subMonths() | أشهر تقويمية؛ ويُقصَّر اليوم إلى طول الشهر الهدف |
addYears() subYears() | مثل 12 شهرًا لكل سنة؛ و30 Esfand في سنة كبيسة يصير 29 Esfand في سنة عادية |
startOfDay() endOfDay() | 00:00:00 و23:59:59 من اليوم نفسه |
startOfMonth() endOfMonth() | اليوم الأول 00:00:00؛ واليوم الأخير 23:59:59 |
startOfYear() endOfYear() | 1 Farvardin 00:00:00؛ وآخر يوم من Esfand عند 23:59:59 |
$x = Jalali::create(1403, 12, 30); // آخر يوم في سنة كبيسة
echo $x->addDays(1); // 1404/01/01 00:00:00
echo $x->subDays(30); // 1403/11/30 00:00:00
echo $x->addHours(30); // 1404/01/01 06:00:00
echo $x->addYears(1); // 1404/12/29 00:00:00 (مقصَّر)
echo $x->subMonths(13); // 1402/11/30 00:00:00
echo Jalali::create(1404, 6, 31)->addMonths(1); // 1404/07/30 00:00:00 (مقصَّر)
$m = Jalali::create(1404, 7, 15, 14, 30, 5);
echo $m->startOfMonth(); // 1404/07/01 00:00:00
echo $m->endOfMonth(); // 1404/07/30 23:59:59
echo $m->startOfYear(); // 1404/01/01 00:00:00
echo $m->endOfYear(); // 1404/12/29 23:59:59
تجاوز النطاق المدعوم، أو الإزاحة بمقدار مبالغ فيه، يُطلق InvalidDateException بدل أن يلتف الحساب حول نفسه:
try { Jalali::create(9377, 1, 1)->addYears(1); }
catch (InvalidDateException $e) { echo $e->getMessage(); }
// Jalali year out of the supported range: 9378
try { $x->addDays(PHP_INT_MAX); }
catch (InvalidDateException $e) { echo $e->getMessage(); }
// Cannot shift a date by 9223372036854775807 days: out of the supported range.
المقارنة والقياس#
تقارن دوال المقارنة (eq ne gt gte lt lte equals isBefore isAfter between isPast isFuture isToday) اللحظات، وتقبل أي CalendarDate أو DateTimeInterface. فلا تتساوى قيمتان إلا إذا كانتا اللحظة نفسها، وهذا يتوقف على المنطقة الزمنية لا على التاريخ المطبوع وحده.
$a = Jalali::create(1405, 1, 1, 0, 0, 0, $tehran);
var_dump($a->eq(new DateTimeImmutable('2026-03-21 00:00', $tehran))); // bool(true)
var_dump($a->between(Jalali::create(1404, 1, 1), Jalali::create(1406, 1, 1))); // bool(true)
| الدالة | الدلالة |
|---|---|
diffInDays($other, $absolute = true) | أيام كاملة، مقطوعة نحو الصفر. ومع false تكون الإشارة إشارة $this - $other |
diffInMonths($other, $absolute = true) | أشهر جلالية كاملة؛ ويُحتسب اليوم ووقت اليوم (من 15 Farvardin إلى 14 Tir شهران) |
diffInYears($other, $absolute = true) | سنوات جلالية كاملة، على أساس الذكرى السنوية |
$jan = Jalali::create(1404, 1, 1);
$feb = Jalali::create(1404, 2, 1);
echo $jan->diffInDays($feb); // 31
echo $jan->diffInDays($feb, false); // -31
echo Jalali::create(1404, 1, 15)->diffInMonths(Jalali::create(1404, 4, 14)); // 2
echo Jalali::create(1380, 5, 5)->diffInYears(Jalali::create(1404, 5, 4)); // 23
echo Jalali::create(1380, 5, 5)->diffInYears(Jalali::create(1404, 5, 5)); // 24
الدوال الساكنة المساعدة#
| الدالة | ما تُرجعه |
|---|---|
Jalali::isValid($y, $m, $d) | bool؛ تُرجع false (ولا تُطلق استثناءً أبدًا) للسنوات أو الأشهر أو الأيام الخارجة عن النطاق |
Jalali::isLeapYear($y) | كبيسة إذا كان $y mod 33 أحد الأعداد 1 و5 و9 و13 و17 و22 و26 و30 |
Jalali::daysInYear($y) | 365 أو 366 |
Jalali::daysInMonth($y, $m) | 31 للأشهر من 1 إلى 6، و30 للأشهر من 7 إلى 11، و29 أو 30 لشهر Esfand؛ و0 لرقم شهر غير صالح |
Jalali::gregorianToJalali($gy, $gm, $gd) | [year, month, day]؛ ويجب أن تكون السنة الميلادية من 1 إلى 9999 |
Jalali::jalaliToGregorian($jy, $jm, $jd) | [year, month, day]؛ يُقبل اليوم من 1 إلى 31 لأي شهر، ويتجاوز الفائض إلى ما بعده |
echo implode(',', array_filter(range(1399, 1410), Jalali::isLeapYear(...))); // 1399,1403,1408
echo Jalali::daysInMonth(1403, 12), ' ', Jalali::daysInMonth(1404, 12); // 30 29
echo Jalali::daysInYear(1403), ' ', Jalali::daysInYear(1404); // 366 365
echo json_encode(Jalali::gregorianToJalali(2026, 3, 21)); // [1405,1,1]
echo json_encode(Jalali::jalaliToGregorian(1405, 1, 1)); // [2026,3,21]
try { Jalali::gregorianToJalali(10000, 1, 1); }
catch (InvalidDateException $e) { echo $e->getMessage(); }
// Gregorian date out of the supported range (years 1-9999): 10000-1-1
ملاحظات عملية#
- المناطق الزمنية. التواريخ المنشأة بلا منطقة تستخدم المنطقة الافتراضية في PHP. مرّر
DateTimeZoneإلىcreate()/make()كلما وجب ألا تعتمد النتيجة على إعدادات الخادم. وتُقرأ الساعة والدقيقة والتاريخ نفسه في منطقة النسخة. - حدود الأيام. يتغيّر اليوم عند منتصف الليل المدني في منطقة النسخة، كما في كل أجزاء هذه المكتبة.
- الاستخدام بوصفها قيمة. النسخ تنفّذ
Stringable، ويمكن مشاركتها وتخزينها في المصفوفات بأمان؛ ورتّبها بحسبgetTimestamp(). - ذو صلة. التحويل إلى التقويمات الأخرى والمقارنة بينها في التحويل والمقارنة بين التواريخ؛ وإضافة تكامل Carbon في ماكروات Carbon؛ والاطلاع على العطل الإيرانية في العطل.