رفتن به متن راهنما
همه‌ی راهنماها

عیب‌یابی و پرسش‌های متداول

جواب پرسش‌هایی که واقعاً پیش می‌آید. تابع کمکی پیدا نمی‌شود، روز هفته، سال در رشته، منطقهٔ زمانی، ماکروهای Carbon، cast در Eloquent، شمارهٔ موبایل، اوقات شرعی و تعطیلات.

برنامه RTLY-Kit 0.2.0آخرین بررسی زمان خواندن 14 دقیقه

در این صفحه

هر جواب با کد تطبیق داده شده و نمونه‌ها اجرا شده‌اند. اگر مشکلتان اینجا نیست، اول مدیریت خطا (معنی هر کد خطا) و سقف‌ها را ببینید.

نصب و توابع کمکی

خطای «Call to undefined function jdate()» می‌گیرم. توابع کمکی کجا هستند؟

توابع کمکی در فضای‌نام RtlyKit هستند. بسته به‌طور پیش‌فرض هیچ تابع سراسری تعریف نمی‌کند تا هیچ‌وقت با کد شما یا بستهٔ دیگر تداخل نکند. تابع‌هایی را که لازم دارید import کنید، با نام کامل صدا بزنید، یا یک بار موقع راه‌اندازی نام‌های کوتاه سراسری را روشن کنید:

PHP
<?php
require __DIR__.'/vendor/autoload.php';

use function RtlyKit\jdate;                       // راه ۱: import
echo jdate('2026-03-21')->format('Y/m/d'), "\n";  // 1405/01/01

echo \RtlyKit\jdate('2026-03-21')->format('Y/m/d'), "\n";  // راه ۲: نام کامل

$skipped = \RtlyKit\Globals::register();          // راه ۳: نام‌های کوتاه سراسری
echo \jdate('2026-03-21')->format('Y/m/d'), "\n";  // 1405/01/01
var_dump($skipped);                               // array(0) {} (نام اشغال‌شده‌ای نبود)

Globals::register() تابع موجود را عوض نمی‌کند و خطا پرتاب نمی‌کند. نام‌هایی را که رد کرده برمی‌گرداند. Laravel این‌ها را برای شما ثبت نمی‌کند. در Blade یا \RtlyKit\jdate(...) را صدا بزنید یا سراسری‌ها را در AppServiceProvider::register() ثبت کنید. توابع کمکی و سراسری را ببینید.

کدام خطا را بگیرم؟

برای کل کتابخانه RtlyKit\Exceptions\RtlyKitThrowable (یا RtlyKitException) را بگیرید، یا یک زیرکلاس مشخص مثل InvalidDateException. اعتبارسنج‌ها اصلاً خطا پرتاب نمی‌کنند و try نمی‌خواهند. روی getErrorCode() تصمیم بگیرید، نه روی متن پیام. جزئیات در مدیریت خطا.

تقویم‌ها

شمارهٔ روز هفته در جلالی و هجری فرق دارد

عمدی است. Jalali::getDayOfWeek() (و توکن w) از شنبه، اولین روز هفتهٔ ایرانی، می‌شمارد: شنبه ۰ و جمعه ۶. Hijri و Hebrew مثل date('w') در PHP از یکشنبه شروع می‌کنند، پس شنبه ۶ است. برای یک روز، 2026-03-21 (شنبه):

PHP
use function RtlyKit\{jdate, hdate, hebrew_date};

echo jdate('2026-03-21')->getDayOfWeek(), ' ',
     hdate('2026-03-21')->getDayOfWeek(), ' ',
     hebrew_date('2026-03-21')->getDayOfWeek(), "\n";   // 0 6 6

اگر شماره‌گذاری یکسان می‌خواهید، از راه میلادی بگیرید: toGregorian()->format('w').

jdate('1405-01-01') و jdate('2026-03-21') هر دو کار می‌کنند، ولی سال میلادی ۹۹۹ نه

رشته‌ای با شکل Y/m/d یا Y-m-d که سالش کمتر از ۱۷۰۰ باشد، تاریخ خود همان تقویم حساب می‌شود (برای Hijri هم همین است. برای Hebrew سال ۳۰۰۰ یا بیشتر). وگرنه میلادی است. پس تاریخ میلادیِ واقعی با سال کوچک اشتباه خوانده می‌شود. به‌جای رشته، یک DateTimeImmutable بدهید که هیچ‌وقت دوباره تفسیر نمی‌شود:

PHP
use function RtlyKit\jdate;

echo jdate('2026-03-21')->format('Y/m/d'), "\n";                              // 1405/01/01 (سال >= 1700: میلادی)
echo jdate('1405-01-01')->toGregorian()->format('Y-m-d'), "\n";               // 2026-03-21 (سال < 1700: جلالی)
echo jdate('0999-01-01')->format('Y/m/d'), "\n";                              // 0999/01/01 (سال جلالی 999)
echo jdate(new DateTimeImmutable('0999-01-01'))->format('Y/m/d'), "\n";       // 0377/10/11 (سال میلادی 999)

رقم‌های فارسی و عربی اول یکسان می‌شوند، پس '۱۴۰۴/۰۱/۰۱' کار می‌کند. رشتهٔ خالی InvalidDateException می‌دهد و null یعنی «همین الان».

format() رقم انگلیسی چاپ می‌کند. رقم فارسی چطور؟

قالب‌بندی عمداً رقم لاتین می‌دهد تا نتیجه برای پایگاه داده و URL امن باشد. موقع نمایش، خروجی را تبدیل کنید:

PHP
use function RtlyKit\{jdate, to_persian};

echo jdate('2026-03-21')->format('Y/m/d'), "\n";               // 1405/01/01
echo to_persian(jdate('2026-03-21')->format('Y/m/d')), "\n";   // ۱۴۰۵/۰۱/۰۱

در Hijri::format() هم می‌توانید با آرگومان $digits (latin، persian یا arabic) رقم را همان‌جا بگیرید.

jdate('1404/12/30') خطا می‌دهد ولی 1403/12/30 نه

اسفند فقط در سال کبیسه ۳۰ روز دارد. ۱۴۰۳ کبیسه است و ۱۴۰۴ نیست. پس ۱۴۰۴/۱۲/۳۰ وجود ندارد و InvalidDateException با کد invalid_date می‌گیرید. ماه ۱۳ یا روز ۳۱ در ماه‌های نیمهٔ دوم سال (مثل ۱۴۰۴/۰۷/۳۱) هم همین‌طور است.

«امروز» برای کاربرانم یک روز فرق دارد

jdate() بدون آرگومان منطقهٔ زمانی پیش‌فرض PHP را به کار می‌برد (date_default_timezone_get()، که روی سرورها اغلب UTC است). نزدیک نیمه‌شب به وقت تهران، روز جلالی یکی قبل یا بعد می‌شود. یک DateTimeZone بدهید یا منطقهٔ پیش‌فرض برنامه را تنظیم کنید. آرگومان منطقه، لحظهٔ ورودی را تبدیل می‌کند:

PHP
use function RtlyKit\jdate;

$utc = new DateTimeImmutable('2026-03-20 22:00', new DateTimeZone('UTC'));

echo jdate($utc)->format('Y/m/d H:i'), "\n";                                       // 1404/12/29 22:00
echo jdate($utc, new DateTimeZone('Asia/Tehran'))->format('Y/m/d H:i'), "\n";    // 1405/01/01 01:30

برای سال خیلی دور یا عدد خیلی بزرگ InvalidDateException می‌گیرم

هر تقویم بازهٔ ثابتی دارد (جلالی ‎-۶۲۰ تا ۹۳۷۷، هجری ۱ تا ۹۶۶۵، عبری ۳۷۶۲ تا ۱۳۷۵۹، میلادی ۱ تا ۹۹۹۹) و مقدارهای خیلی بزرگ در add*() و زمان یونیکس رد می‌شوند. عمدی است: خطا می‌گیرید، نه تاریخ سرریزشده. سقف‌ها را ببینید.

تاریخ هجری یک روز با اعلام محلی ما فرق دارد

Hijri از تقویم ام‌القری (تقویم مدنی عربستان) استفاده می‌کند که از قبل حساب می‌شود. مرجع‌های محلی رؤیت هلال، از جمله ایران، ممکن است ماه را یکی دو روز دیرتر شروع کنند. جدول داخلی سال‌های ۱۳۰۰ تا ۱۵۰۰ هجری قمری را دارد و ۱۳۱۸ تا ۱۵۰۰ با تقویم رسمی KACST مقایسه شده است. بیرون از ۱۳۰۰ تا ۱۵۰۰ و برای HijriVariant::Tabular تاریخ حسابی است و ممکن است یکی دو روز با رؤیت فرق کند. تقویم هجری و دقت و داده را ببینید.

Carbon و Laravel

روی شیء Carbon خطای «undefined method toJalali()» می‌گیرم

ماکروهای Carbon (toJalali، jformat، createFromJalali، toHijri، toHebrew، createFromHijri، createFromHebrew) فقط وقتی ثبت می‌شوند که nesbot/carbon (نسخهٔ ۳) نصب باشد. Composer آن را اجباری نمی‌کند، پس خودتان نصب کنید: composer require nesbot/carbon. ماکروها وقتی ثبت می‌شوند که فایل توابع کمکی بسته با بارگذار خودکار Composer بارگذاری شود (و دوباره موقع boot در provider لاراول)، برای Carbon و CarbonImmutable. کلاس Illuminate\Support\Carbon در Laravel از Carbon ارث می‌برد، پس آن هم کار می‌کند:

PHP
use Carbon\Carbon;

echo Carbon::parse('2026-03-21')->toJalali()->format('Y/m/d'), "\n";   // 1405/01/01
echo Carbon::createFromJalali(1405, 1, 1)->toDateString(), "\n";       // 2026-03-21
var_dump(Carbon::hasMacro('toJalali'));                                // bool(true)

اگر hasMacro() مقدار false داد، Carbon نصب نیست یا نسخهٔ major آن پشتیبانی نمی‌شود. ماکروهای Carbon را ببینید.

قانون‌های اعتبارسنجی Laravel (national_code، sheba و ...) پیدا نمی‌شوند

با شناسایی خودکار، service provider خودش ثبت می‌شود. اگر شناسایی خودکار را برای این بسته خاموش کرده‌اید (dont-discover) یا پیکربندی کش‌شدهٔ قدیمی دارید، RtlyKit\Laravel\RtlyKitServiceProvider را دستی ثبت کنید و کش‌ها را پاک کنید. نام قانون‌ها: national_code، sheba، bank_card، iran_mobile (با نام کوتاه mobile)، postal_code و vehicle_plate. پیام‌ها به فارسی و انگلیسی و عربی همراه بسته‌اند و از app()->getLocale() پیروی می‌کنند. خط‌های lang/{locale}/validation.php خودتان باز هم اولویت دارند. راه‌اندازی Laravel را ببینید.

مدل Eloquent موقع نوشتن تاریخ InvalidDateException می‌دهد

JalaliCast این‌ها را قبول می‌کند: یک Jalali، هر DateTimeInterface، زمان یونیکس، رشتهٔ میلادی، یا رشتهٔ جلالی مثل 1404/01/15 10:30 (رقم فارسی و جداکنندهٔ / یا - مجاز است). هر چیز دیگر خطاست: تاریخ غیرممکن، متن بی‌معنی و مقدار غیرسکالر (مثل آرایه) موقع نوشتن InvalidDateException می‌دهند. null و رشتهٔ خالی null می‌شوند. آن را در form request یا controller بگیرید:

PHP
$post->published_at = '1404/01/15 10:30';   // ذخیره می‌شود: 2025-04-04 10:30:00
$post->published_at = '1404/13/45';         // InvalidDateException (invalid_date)
$post->published_at = 'hello';              // InvalidDateException (invalid_date)
$post->published_at = ['x'];                // InvalidDateException (invalid_date)

ستون را یک datetime میلادی معمولی نگه دارید. cast مقدار Y-m-d H:i:s را می‌نویسد و موقع خواندن یک Jalali تغییرناپذیر می‌دهد. cast منطقهٔ زمانی را تبدیل نمی‌کند. قانون‌های اعتبارسنجی و cast را ببینید.

اعتبارسنج‌ها و داده

is_national_code(13542419) برابر false است ولی با رشته true می‌شود

یک int صفرهای اولش را از قبل از دست داده: 0013542419 شده 13542419، هشت رقم. اعتبارسنج‌ها عدد صحیح را به رشته تبدیل می‌کنند ولی صفرها را نمی‌توانند برگردانند. شناسه‌ها را همه‌جا (فیلد فرم، JSON، ستون پایگاه داده) رشته نگه دارید:

PHP
use function RtlyKit\is_national_code;

var_dump(is_national_code(13542419));       // bool(false)
var_dump(is_national_code('0013542419'));   // bool(true)

برای کارت یا شبای معتبر، getBankName() مقدار null می‌دهد

جدول‌های BIN و کد شبا و اپراتور فقط مواردی را دارند که دست‌کم دو منبع تأییدشان کرده‌اند. پس عمداً کامل نیستند (۳۹ BIN و ۳۸ کد شبا). null یعنی «نمی‌دانیم»، نه «نامعتبر». معتبر بودن کارت از چک‌سام Luhn می‌آید و معتبر بودن شبا از mod-97، مستقل از جدول‌ها. دربارهٔ NationalCode::getLocation() (۵۴۷ پیش‌شماره) هم همین است و محلی که می‌دهد جای صدور است، نه زادگاه. کاربر را به‌خاطر null بودن یک نام رد نکنید. دقت و داده را ببینید.

چرا اعتبارسنج برای null یا آرایه هم خطا نمی‌دهد؟

عمدی است. اعتبارسنج‌ها mixed می‌گیرند و با false یا یک Result نامعتبر جواب می‌دهند (invalid_type برای مقدار غیرسکالر، input_too_long بالای ۴۰۹۶ بایت). پس می‌توانید ورودی خام درخواست را مستقیم بدهید. مرور اعتبارسنج‌ها را ببینید.

شمارهٔ موبایل معتبر است ولی allocated برابر false است

اعتبار موبایل فقط شکل شماره را می‌سنجد. allocated می‌گوید پیش‌شماره در بلوک‌های موبایلِ طرح شماره‌گذاری ملی هست یا نه. برای شمارهٔ معتبری که پیش‌شمارهٔ آن در طرح نیست false می‌شود، مثلاً 09061234567. ممکن است پیش‌شماره تازه باشد و در داده نیامده باشد. این به معنی نامعتبر بودن شماره نیست، پس کاربر را به‌خاطر آن رد نکنید.

PHP
use RtlyKit\Validation\Mobile;

var_dump(Mobile::isValid('09061234567'));       // bool(true)
var_dump(Mobile::isAllocated('09061234567'));   // bool(false)
var_dump(Mobile::isAllocated('09121234567'));   // bool(true)

جزئیات در موبایل، کدپستی، پلاک.

عدد و متن

format_number() یک float را تا ۱۵ رقم نشان می‌دهد

float در PHP بیشتر اعشارها را دقیق نگه نمی‌دارد. برای همین قالب‌بند، float را با کوتاه‌ترین متن اعشاری می‌نویسد که دوباره همان عدد را بدهد، حداکثر با ۱۵ رقم معنادار. 1234567.891 تمیز چاپ می‌شود، ولی 1 / 3 پانزده رقم سه می‌دهد و 123456789.123456789 به شکل float رقم‌های آخرش را از دست می‌دهد. برای رقم بیشتر، عدد را به شکل رشته بدهید:

PHP
use function RtlyKit\format_number;

echo format_number(1234567.891), "\n";                // ۱٬۲۳۴٬۵۶۷٫۸۹۱
echo format_number(123456789.123456789), "\n";        // ۱۲۳٬۴۵۶٬۷۸۹٫۱۲۳۴۵۷ (float، ۱۵ رقم)
echo format_number('123456789.123456789'), "\n";      // ۱۲۳٬۴۵۶٬۷۸۹٫۱۲۳۴۵۶۷۸۹ (رشته، دقیق)

number_to_words() برای 1.5، برای عدد خیلی بزرگ و برای زبان‌های دیگر خطا می‌دهد

عدد به حروف فقط برای عدد صحیح تعریف شده (1.5 و NaN و INF خطای InvalidNumberException می‌دهند. floatِ صحیح مثل 3.0 قبول است). فارسی تا ۲۱ رقم می‌رود و عربی تا 1027. فقط fa و ar هست. زبان دیگر UnsupportedLocaleException می‌دهد. عدد به حروف، عدد به حروف عربی و سقف‌ها را ببینید.

اوقات شرعی و تعطیلات

اوقات شرعی در منطقهٔ زمانی اشتباه است یا یک ساعت فرق دارد

وقت‌ها به شکل HH:MM و به وقت محلی منطقهٔ زمانی محاسبه‌گر هستند، برای روز تقویمی محلیِ تاریخی که می‌دهید. PrayerTimes::forCity() منطقهٔ خود شهر را به کار می‌برد. محاسبه‌گری که با new PrayerTimes() و بدون منطقه ساخته شود از پیش‌فرض PHP استفاده می‌کند (date_default_timezone_get()، که در یک کانتینر تازه UTC است). پس یک DateTimeZone بدهید. ساعت تابستانی برای هر رویداد جدا اعمال می‌شود. پس جدولی که از تغییر ساعت رد می‌شود در دو طرف درست است (ظهر قاهره در 2026-10-08 و 2026-10-31):

PHP
use RtlyKit\Prayer\PrayerTimes;

$cairo = PrayerTimes::forCity('cairo', PrayerTimes::METHOD_EGYPT);

echo $cairo->getTimes(new DateTimeImmutable('2026-10-08'))['dhuhr'], ' ',   // 12:43 (ساعت تابستانی)
     $cairo->getTimes(new DateTimeImmutable('2026-10-31'))['dhuhr'], "\n";   // 11:39 (ساعت استاندارد)

تاریخی که در منطقهٔ دیگری بدهید اول به منطقهٔ محاسبه‌گر تبدیل می‌شود. 2026-03-20 23:30 به وقت UTC و 2026-03-21 03:00 به وقت Asia/Tehran هر دو اوقات ۲۱ مارس را برای تهران می‌دهند.

بعضی از اوقات شرعی null هستند

در عرض‌های بالا گاهی خورشید در یک روز به زاویهٔ لازم نمی‌رسد. با قاعدهٔ پیش‌فرض (HighLatitudeRule::AngleBased) صبح و عشا مقدار می‌گیرند. فقط طلوع و غروب، وقتی خورشید واقعاً طلوع یا غروب نمی‌کند، null می‌مانند (و عصر در شب قطبی). با HighLatitudeRule::None هر وقتی که زاویه‌اش به دست نیاید null است. این یک نتیجه است، نه خطا. در رابط کاربری آن را در نظر بگیرید:

PHP
use RtlyKit\Prayer\HighLatitudeRule;
use RtlyKit\Prayer\PrayerTimes;

$oslo = new PrayerTimes(69.65, 18.96, PrayerTimes::METHOD_MWL, PrayerTimes::ASR_STANDARD, new DateTimeZone('Europe/Oslo'));
$day  = new DateTimeImmutable('2026-06-21');

echo json_encode($oslo->getTimes($day)), "\n";
// {"fajr":"03:10","sunrise":null,"dhuhr":"12:46","asr":"17:58","maghrib":null,"isha":"22:10"}
echo json_encode($oslo->withHighLatitudeRule(HighLatitudeRule::None)->getTimes($day)), "\n";
// {"fajr":null,"sunrise":null,"dhuhr":"12:46","asr":"17:58","maghrib":null,"isha":null}

جزئیات در عرض‌های جغرافیایی بالا.

nextPrayer() زمانی می‌دهد که مال فرداست

بعد از عشا، nextPrayer() به صبح فردا می‌رسد. در نتیجه کلید date (به شکل Y-m-d) هست که می‌گوید آن نماز مال کدام روز است:

PHP
use RtlyKit\Prayer\PrayerTimes;

$next = PrayerTimes::forCity('tehran')->nextPrayer(new DateTimeImmutable('2026-03-21 23:00', new DateTimeZone('Asia/Tehran')));
echo json_encode($next), "\n";   // {"name":"fajr","time":"04:42","date":"2026-03-22"}

اوقات من چند دقیقه با مسجد یا برنامهٔ دیگری فرق دارد

مرجع‌های مختلف زاویه‌های گرگ‌ومیش و گرد کردن متفاوتی دارند. در روش‌هایی که سنجیده‌ایم، محاسبه تا حدود یک دقیقه با جدول‌های منتشرشده می‌خواند (چه چیزهایی را بررسی کرده‌ایم). روشی را انتخاب کنید که جامعهٔ شما دنبال می‌کند، ضریب عصر را تنظیم کنید (ASR_HANAFI عصر را دیرتر می‌کند) و برای جاهای مرتفع ارتفاع را بدهید. اگر مرجع محلی شما چند دقیقه احتیاط اضافه می‌کند، با withTune() همان دقیقه‌ها را به هر وقت اضافه کنید (حداکثر ۳۰ دقیقه). اوقات شرعی و دقت و داده را ببینید.

یک مناسبت مذهبی یک روز با تقویم رسمی فرق دارد

برای ۱۳۹۴ و ۱۳۹۶ تا ۱۴۰۵ تاریخ‌های رسمی داریم و خطایی انتظار نمی‌رود. در سال‌های تخمینی، مناسبت‌های اسلامی از تقویم هجری حساب می‌شوند و ایران آن‌ها را با رؤیت هلال اعلام می‌کند. پس ممکن است ۱ یا ۲ روز فرق کنند. تعطیلات ثابت جلالی (مثل نوروز و ۲۲ بهمن) دقیق‌اند. با IranHolidays::sourceOf($year) ببینید هر سال از کدام منبع است. اگر هلال دیده شد، با HolidayCalendar::withHijriMonthStart() تاریخ واقعی را بدهید، یا با withIslamicOffset() یک جابه‌جایی ثابت بگذارید. تقویم تعطیلات و تعطیلات را ببینید.