در این صفحه
این کلاس چه میکند#
کلاس RtlyKit\Calendar\Jalali یک تاریخ و زمان در تقویم جلالی (شمسی) است و تغییر نمیکند. زیر کار یک DateTimeImmutable نگه میدارد. برای همین منطقهٔ زمانی، زمان یونیکس و معادل دقیق میلادی دارد. سال و ماه و روز جلالی از همان لحظه حساب میشوند. هر متدی که تاریخ را عوض کند، شیء تازه میدهد و شیء اصلی دستنخورده میماند. کلاس 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 رد میشود، چون ۱۴۰۴ کبیسه نیست، ولی 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 دیگر (هجری، عبری، جلالی)، زمان یونیکس (عدد صحیح)، رشته یا 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داشته باشد، سالش ۳ یا ۴ رقم باشد (ساعتH:iیاH:i:sبعد از فاصله یاTاختیاری است) و سال کمتر از ۱۷۰۰ باشد، تاریخ جلالی حساب میشود. - هر رشتهٔ دیگر، از جمله هر سال ۱۷۰۰ و بالاتر، به
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' تاریخ جلالی خوانده میشود، نه میلادی. برای تاریخ میلادیِ قبل از ۱۷۰۰ خودتان یک 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 میخواند (رقمها میتوانند فارسی باشند)، ولی سال و ماه و روز را جلالی میگیرد. قاعدههای میلادی، مثل «فوریه حداکثر ۲۹ روز دارد»، اینجا اثر ندارند. خود تقویم جلالی نتیجه را چک میکند.
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 پشتیبانی میشود. این بازه دقیقاً با سالهای میلادی ۱ تا ۹۹۹۹ برابر است. اولین روز -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 میدهد. زمان یونیکسی که از سالهای میلادی ۱ تا ۹۹۹۹ بیرون باشد پیام Timestamp out of the supported range: ... میدهد.
برای خطاهای تقویم فقط InvalidDateException را بگیرید. برای همهٔ خطاهای کتابخانه کلاس پایهٔ RtlyKit\Exceptions\RtlyKitException (یا اینترفیس RtlyKitThrowable) را بگیرید. بیشتر در مدیریت خطا.
خواندن مقدارها#
| متد | خروجی |
|---|---|
getYear()، getMonth()، getDay() | سال، ماه (۱ تا ۱۲) و روز (۱ تا ۳۱) جلالی |
getHour()، getMinute()، getSecond() | ساعت، دقیقه و ثانیه در منطقهٔ زمانی شیء |
getDayOfWeek() | ۰ = شنبه تا ۶ = جمعه |
monthName() | نام فارسی ماه، مثلاً مهر |
getTimestamp()، getTimezone() | زمان یونیکس و DateTimeZone |
toGregorian() | همان DateTimeImmutable زیرین |
toDateString()، toDateTimeString()، __toString() | Y/m/d، Y/m/d H:i:s، Y/m/d H:i:s |
شمارهٔ روز هفته#
هفتهٔ جلالی از شنبه شروع میشود. getDayOfWeek() و توکن w برای شنبه ۰ و برای جمعه ۶ میدهند. توکن N از ۱ (شنبه) تا ۷ (جمعه) است. هجری و عبری فرق دارند. آنها از یکشنبه شروع میکنند (۰ = یکشنبه)، مثل 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() را در تقویم جلالی حساب میکند. اگر آرگومان دوم true باشد، همهٔ رقمها فارسی میشوند. بکاسلش نویسهٔ بعدی را همانطور که هست مینویسد. نویسههایی که توکن نیستند عیناً کپی میشوند. الگوی بلندتر از Jalali::MAX_FORMAT_LENGTH (۲۵۶ بایت) InvalidDateException با کد input_too_long میدهد. بکاسلشِ آخر الگو نادیده گرفته میشود.
$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 | سال، چهار رقمی و دو رقمی |
m n | ماه، با صفر ابتدایی و بدون آن |
F M | نام فارسی ماه (M همان F است، چون نام ماههای فارسی کوتاهشده ندارند) |
d j | روز، با صفر ابتدایی و بدون آن |
l D | نام روز هفته. D شکل تکحرفی است (ش ی د س چ پ ج) |
w N | شمارهٔ روز هفته از شنبه: ۰ تا ۶ و ۱ تا ۷ |
z | شمارهٔ روز در سال جلالی، از صفر |
t L | تعداد روزهای ماه. و ۱ اگر سال کبیسه باشد |
H G h g | ساعت ۲۴ و ۱۲ ساعته، با صفر ابتدایی و بدون آن |
i s | دقیقه و ثانیه |
a A | ق.ظ/ب.ظ و قبل از ظهر/بعد از ظهر |
S | همیشه خالی (فارسی پسوند ترتیبی ندارد) |
W | شمارهٔ هفتهٔ ISO-8601 برای لحظهٔ میلادی زیرین |
c | Y-m-d\TH:i:sP با تاریخ جلالی |
r | رشتهٔ RFC 2822 از لحظهٔ میلادی (این استاندارد نامهای انگلیسی میخواهد) |
U e T P p O Z I u v | از لحظهٔ زیرین گرفته میشوند (زمان یونیکس، منطقه، اختلاف ساعت، میکروثانیه و ...) |
جابهجایی و برش#
هر متد شیء تازه میدهد و منطقهٔ زمانی را نگه میدارد.
| متد | رفتار |
|---|---|
addDays() subDays() | روز کامل روی لحظهٔ زیرین |
addHours() addMinutes() addSeconds() و sub* | زمان واقعی سپریشده |
addMonths() subMonths() | ماه تقویمی. اگر ماه مقصد کوتاهتر باشد، روز کم میشود تا در ماه جا شود |
addYears() subYears() | هر سال معادل ۱۲ ماه است. ۳۰ اسفندِ سال کبیسه در سال غیرکبیسه ۲۹ اسفند میشود |
startOfDay() endOfDay() | 00:00:00 و 23:59:59 همان روز |
startOfMonth() endOfMonth() | اول ماه 00:00:00، آخر ماه 23:59:59 |
startOfYear() endOfYear() | اول فروردین 00:00:00، آخر اسفند 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) | تعداد ماه کامل جلالی. روز و ساعت هم حساب میشود (۱۵ فروردین تا ۱۴ تیر میشود ۲ ماه) |
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 بر ۳۳ یکی از 1، 5، 9، 13، 17، 22، 26، 30 باشد، کبیسه است |
Jalali::daysInYear($y) | ۳۶۵ یا ۳۶۶ |
Jalali::daysInMonth($y, $m) | ۳۱ برای ماههای ۱ تا ۶، ۳۰ برای ۷ تا ۱۱، و ۲۹ یا ۳۰ برای اسفند. برای شمارهٔ ماه نامعتبر 0 |
Jalali::gregorianToJalali($gy, $gm, $gd) | [سال, ماه, روز]. سال میلادی باید ۱ تا ۹۹۹۹ باشد |
Jalali::jalaliToGregorian($jy, $jm, $jd) | [سال, ماه, روز]. روز ۱ تا ۳۱ برای هر ماه قبول میشود و اضافهاش به ماه بعد میرود |
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 را میگیرد. اگر نتیجه نباید به تنظیمات سرور وابسته باشد، به
create()یاmake()یکDateTimeZoneبدهید. ساعت و دقیقه و خود تاریخ در منطقهٔ همان شیء خوانده میشوند. - مرز روز. روز در نیمهشبِ منطقهٔ شیء عوض میشود.
- مثل یک مقدار. شیءها
Stringableهستند. میتوانید راحت آنها را به اشتراک بگذارید و در آرایه نگه دارید. برای مرتبکردن ازgetTimestamp()استفاده کنید. - ببینید. تبدیل به تقویمهای دیگر و مقایسهٔ بین تقویمها: تبدیل و مقایسهٔ تاریخها. Carbon: ماکروهای Carbon. تعطیلات رسمی ایران: تعطیلات.