# morilog/jalali - Jalali (Persian/Iranian) Calendar Library for PHP ## Introduction The morilog/jalali package is a comprehensive PHP library designed for working with the Jalali (Shamsi or Iranian) calendar system. The Jalali calendar is a solar calendar used in Iran and Afghanistan, offering precise astronomical calculations based on the algorithm provided by Kazimierz M. Borkowski. This library provides seamless conversion between Gregorian and Jalali calendars, enabling developers to handle Persian dates with the same ease as working with standard PHP DateTime objects. The library features an immutable, fluent API for date manipulation, comparison, and formatting. Built on top of Carbon, it provides familiar methods for adding/subtracting time units, comparing dates, and formatting output in Persian. Core functionality includes calendar conversion utilities, date validation, leap year detection, and Persian/Afghan month name localization. The library is particularly valuable for applications targeting Iranian or Afghan users, financial systems operating on the Iranian fiscal calendar, or any system requiring accurate Jalali date handling. ## API Documentation and Code Examples ### Create Jalalian Instance from Current Time Create a Jalalian date object representing the current moment, optionally with a specific timezone. ```php toString(); // Output: 1403-08-29 14:30:45 // With timezone $tehran = new \DateTimeZone('Asia/Tehran'); $nowInTehran = Jalalian::now($tehran); echo $nowInTehran->format('Y-m-d H:i:s'); // Output: 1403-08-29 14:30:45 // Using helper function $date = jdate(); echo $date->toString(); // Output: 1403-08-29 14:30:45 ``` ### Create Jalalian Instance from Specific Date Instantiate a Jalalian date with specific year, month, day, and optional time components. ```php format('Y-m-d'); // Output: 1397-06-24 // With time components $dateTime = new Jalalian(1397, 6, 24, 15, 30, 45); echo $dateTime->format('Y-m-d H:i:s'); // Output: 1397-06-24 15:30:45 // Validation is automatic - throws exception on invalid dates try { $invalid = new Jalalian(1397, 13, 1); // Invalid month } catch (\Assert\InvalidArgumentException $e) { echo "Invalid date: " . $e->getMessage(); } ``` ### Create from Timestamp or String Create Jalalian instances from Unix timestamps or human-readable date strings. ```php toString(); // Output: 1391-01-20 04:00:00 // Using helper function with timestamp $date2 = jdate(1333857600); echo $date2->format('Y-m-d'); // Output: 1391-01-20 // From human-readable strings (using strtotime) $lastSunday = Jalalian::forge('last sunday'); echo $lastSunday->format('%B %d، %Y'); // Output: دی 02، 1391 $yesterday = Jalalian::fromDateTime('yesterday'); echo $yesterday->format('Y-m-d'); // Output: 1403-08-28 $today = Jalalian::forge('today'); echo $today->format('%A, %d %B %y'); // Output: جمعه، 23 اسفند 97 ``` ### Create from Carbon or DateTime Convert Carbon or DateTime objects to Jalalian dates. ```php format('Y-m-d H:i:s'); // Output: 1397-01-18 12:00:00 // From DateTime $dateTime = new \DateTime('2016-05-08'); $jalali = Jalalian::fromDateTime($dateTime); echo $jalali->format('Y/m/d'); // Output: 1395/02/19 // From DateTime string $jalali = Jalalian::fromDateTime('2018-04-07 12:00:00'); echo $jalali->toString(); // Output: 1397-01-18 12:00:00 ``` ### Create from Jalali Format String Parse a Jalali date string using a specific format pattern. ```php getYear(); // Output: 1397 echo $jalali->getMonth(); // Output: 1 echo $jalali->getDay(); // Output: 18 // Parse from different format $jalali2 = Jalalian::fromFormat('Y/m/d', '1395/02/19'); echo $jalali2->format('Y-m-d'); // Output: 1395-02-19 // With timezone $tehran = new \DateTimeZone('Asia/Tehran'); $jalali3 = Jalalian::fromFormat('Y-m-d', '1397-06-15', $tehran); echo $jalali3->getTimezone()->getName(); // Output: Asia/Tehran ``` ### Add Time Units (Years, Months, Days, Hours, Minutes, Seconds) Add various time units to a Jalalian date, returning a new immutable instance. ```php addYears(1); echo $nextYear->format('Y'); // Output: 1398 // Add months $nextMonth = $date->addMonths(1); echo $nextMonth->format('m'); // Output: 02 // Add multiple months (handles year overflow) $future = $date->addMonths(15); echo $future->format('Y-m'); // Output: 1398-04 // Add days $tomorrow = $date->addDays(1); echo $tomorrow->format('d'); // Output: 19 // Add hours $laterHour = $date->addHours(1); echo $laterHour->format('H'); // Output: 13 // Add minutes $laterMinutes = $date->addMinutes(10); echo $laterMinutes->format('i'); // Output: 20 // Add seconds $laterSeconds = $date->addSeconds(10); echo $laterSeconds->format('s'); // Output: 10 // Chain multiple operations $complex = $date->addYears(2)->addMonths(3)->addDays(15)->addHours(5); echo $complex->format('Y-m-d H:i:s'); // Output: 1399-05-03 17:10:00 ``` ### Subtract Time Units Subtract various time units from a Jalalian date. ```php subYears(1); echo $lastYear->toString(); // Output: 1396-01-18 00:00:00 // Subtract months $lastMonth = $date->subMonths(1); echo $lastMonth->toString(); // Output: 1396-12-18 00:00:00 // Subtract days $tenDaysAgo = $date->subDays(10); echo $tenDaysAgo->format('d'); // Output: 08 // Subtract hours $earlierHour = $date->subHours(1); echo $earlierHour->format('H'); // Output: 11 // Subtract minutes $earlierMinutes = $date->subMinutes(10); echo $earlierMinutes->format('i'); // Output: 00 // Subtract seconds $earlierSeconds = $date->subSeconds(10); echo $earlierSeconds->format('i:s'); // Output: 09:50 // Complex subtraction $past = $date->subYears(2)->subMonths(6)->subDays(5); echo $past->format('Y-m-d'); // Output: 1394-06-13 ``` ### Date Comparison (Equals, Greater Than, Less Than) Compare Jalalian dates or compare with Carbon instances. ```php equalsTo($date2); var_dump($isEqual); // Output: bool(false) $now1 = Jalalian::now(); $now2 = Jalalian::now(); var_dump($now1->equalsTo($now2)); // Output: bool(true) // Greater than comparison $isFuture = Jalalian::now()->greaterThan(Jalalian::now()->subDays(1)); var_dump($isFuture); // Output: bool(true) // Less than comparison $isPast = Jalalian::now()->lessThan(Jalalian::now()->addDays(1)); var_dump($isPast); // Output: bool(true) // Greater than or equal $isGreaterOrEqual = Jalalian::now()->greaterThanOrEqualsTo(Jalalian::now()->subDays(1)); var_dump($isGreaterOrEqual); // Output: bool(true) // Less than or equal $isLessOrEqual = Jalalian::now()->lessThanOrEqualsTo(Jalalian::now()); var_dump($isLessOrEqual); // Output: bool(true) // Compare with Carbon $carbon = Carbon::now(); $jalali = Jalalian::now(); var_dump($jalali->equalsToCarbon($carbon)); // Output: bool(true) var_dump($jalali->greaterThanCarbon($carbon->subDays(1))); // Output: bool(true) var_dump($jalali->lessThanCarbon($carbon->addDays(1))); // Output: bool(true) ``` ### Check Date Properties (Is Today, Is Weekend, Day of Week) Check various properties and states of a Jalalian date. ```php isSaturday()); // Output: bool(true) var_dump($date->isStartOfWeek()); // Output: bool(true) // Check if Friday (end of Persian week) var_dump($date->isFriday()); // Output: bool(false) var_dump($date->isEndOfWeek()); // Output: bool(false) // Check specific day of week (0=Saturday, 6=Friday) var_dump($date->isDayOfWeek(0)); // Output: bool(true) // Get numeric day of week echo $date->getDayOfWeek(); // Output: 0 // Check other days var_dump($date->isSunday()); // Output: bool(false) var_dump($date->isMonday()); // Output: bool(false) var_dump($date->isTuesday()); // Output: bool(false) var_dump($date->isWednesday()); // Output: bool(false) var_dump($date->isThursday()); // Output: bool(false) // Check temporal relationships $today = Jalalian::now(); var_dump($today->isToday()); // Output: bool(true) var_dump($today->addDays(1)->isTomorrow()); // Output: bool(true) var_dump($today->subDays(1)->isYesterday()); // Output: bool(true) var_dump($today->addDays(2)->isFuture()); // Output: bool(true) var_dump($today->subDays(2)->isPast()); // Output: bool(true) ``` ### Get Date Components and Properties Retrieve individual components and properties of a Jalalian date. ```php getYear(); // Output: 1397 echo $date->getMonth(); // Output: 6 echo $date->getDay(); // Output: 24 echo $date->getHour(); // Output: 15 echo $date->getMinute(); // Output: 30 echo $date->getSecond(); // Output: 45 // Get timezone $tz = $date->getTimezone(); echo $tz->getName(); // Output: UTC (or default timezone) // Get day of year (1-365/366) echo $date->getDayOfYear(); // Output: 179 // Get day of week (0=Saturday, 6=Friday) echo $date->getDayOfWeek(); // Output: 0 // Get number of days in current month echo $date->getMonthDays(); // Output: 30 // Get number of days in specific month echo $date->getDaysOf(1); // Output: 31 (Farvardin has 31 days) echo $date->getDaysOf(12); // Output: 29 or 30 (depends on leap year) // Check if leap year var_dump($date->isLeapYear()); // Output: bool(false) // Get quarter (1-4) echo $date->getQuarter(); // Output: 2 // Get week of month echo $date->getWeekOfMonth(); // Output: 4 // Get Unix timestamp echo $date->getTimestamp(); // Output: 1536969600 ``` ### Get Start and End of Period Get the first or last day of various time periods (week, month, quarter, year). ```php getFirstDayOfWeek(); echo $startOfWeek->format('Y-m-d'); // Output: 1397-06-24 // Get last day of week (Friday) $endOfWeek = $date->getEndDayOfWeek(); echo $endOfWeek->format('Y-m-d'); // Output: 1397-06-30 // Get first day of month $startOfMonth = $date->getFirstDayOfMonth(); echo $startOfMonth->format('Y-m-d'); // Output: 1397-06-01 // Get last day of month $endOfMonth = $date->getEndDayOfMonth(); echo $endOfMonth->format('Y-m-d'); // Output: 1397-06-30 // Get first day of year $startOfYear = $date->getFirstDayOfYear(); echo $startOfYear->format('Y-m-d'); // Output: 1397-01-01 // Get last day of year $endOfYear = $date->getEndDayOfYear(); echo $endOfYear->format('Y-m-d'); // Output: 1397-12-29 // Get first day of quarter $startOfQuarter = $date->getFirstDayOfQuarter(); echo $startOfQuarter->format('Y-m-d'); // Output: 1397-04-01 // Get last day of quarter $endOfQuarter = $date->getEndDayOfQuarter(); echo $endOfQuarter->format('Y-m-d'); // Output: 1397-06-31 ``` ### Navigate Periods (Next/Last Week/Month) Navigate to adjacent time periods using convenience methods. ```php getNextWeek(); echo $nextWeek->format('Y-m-d'); // Output: 1397-07-01 // Get last week (subtract 7 days) $lastWeek = $date->getLastWeek(); echo $lastWeek->format('Y-m-d'); // Output: 1397-06-17 // Get next month $nextMonth = $date->getNextMonth(); echo $nextMonth->format('Y-m-d'); // Output: 1397-07-24 // Get last month $lastMonth = $date->getLastMonth(); echo $lastMonth->format('Y-m-d'); // Output: 1397-05-24 // Alias methods for single day $tomorrow = $date->addDay(); $yesterday = $date->subDay(); ``` ### Format Date Output Format Jalalian dates using various format strings and predefined formats. ```php format('Y-m-d H:i:s'); // Output: 1397-01-18 12:10:45 // Custom formats echo $date->format('Y/m/d'); // Output: 1397/01/18 echo $date->format('d-m-Y'); // Output: 18-01-1397 echo $date->format('H:i'); // Output: 12:10 // Persian text formats with strftime-style echo $date->format('%B %d، %Y'); // Output: فروردین 18، 1397 echo $date->format('%A, %d %B %y'); // Output: شنبه، 18 فروردین 97 // Predefined formats echo $date->toString(); // Output: 1397-01-18 12:10:45 echo (string)$date; // Output: 1397-01-18 12:10:45 (__toString) // Converter trait methods echo $date->toDateString(); // Output: 1397/01/18 echo $date->toFormattedDateString(); // Output: 18 فروردین 1397 echo $date->toFormattedDayDateString(); // Output: شنبه 18 فروردین 1397 echo $date->toTimeString(); // Output: 12:10:45 echo $date->toDateTimeString(); // Output: 1397/01/18 12:10:45 echo $date->toDateTimeLocalString(); // Output: 1397-01-18T12:10:45 echo $date->toDayDateTimeString(); // Output: شنبه 18 فروردین 1397 12:10:45 ``` ### Relative Time (Ago Format) Get relative time representation in Persian. ```php ago(); // Output: 10 دقیقه پیش // Hours ago $twoHoursAgo = Jalalian::forge('now - 2 hours'); echo $twoHoursAgo->ago(); // Output: 2 ساعت پیش // Days ago $threeDaysAgo = Jalalian::forge('now - 3 days'); echo $threeDaysAgo->ago(); // Output: 3 روز پیش // Weeks ago $oneWeekAgo = Jalalian::forge('now - 1 week'); echo $oneWeekAgo->ago(); // Output: 1 هفته پیش // Months ago $twoMonthsAgo = Jalalian::forge('now - 2 months'); echo $twoMonthsAgo->ago(); // Output: 2 ماه پیش // Years ago $oneYearAgo = Jalalian::forge('now - 1 year'); echo $oneYearAgo->ago(); // Output: 1 سال پیش ``` ### Convert to Carbon or Array Convert Jalalian dates to Carbon instances or array representation. ```php toCarbon(); echo $carbon->toDateTimeString(); // Output: 2018-09-15 15:30:45 echo $carbon->format('Y-m-d'); // Output: 2018-09-15 // Use Carbon API $carbonNext = $carbon->addDays(7); echo $carbonNext->format('Y-m-d'); // Output: 2018-09-22 // Convert to array $array = $jalali->toArray(); print_r($array); /* Output: Array ( [year] => 1397 [month] => 6 [day] => 24 [dayOfWeek] => 0 [dayOfYear] => 179 [hour] => 15 [minute] => 30 [second] => 45 [micro] => 0 [timestamp] => 1537024245 [formatted] => 1397-06-24 15:30:45 [timezone] => DateTimeZone Object ) */ ``` ### CalendarUtils: Convert Between Jalali and Gregorian Use static methods to convert dates between Jalali and Gregorian calendars. ```php 1395, [1] => 2, [2] => 18) echo "{$jalaliDate[0]}/{$jalaliDate[1]}/{$jalaliDate[2]}"; // Output: 1395/2/18 // Convert Jalali to Gregorian $gregorianDate = CalendarUtils::toGregorian(1395, 2, 18); print_r($gregorianDate); // Output: Array([0] => 2016, [1] => 5, [2] => 7) echo "{$gregorianDate[0]}-{$gregorianDate[1]}-{$gregorianDate[2]}"; // Output: 2016-5-7 // Convert Jalali to Gregorian DateTime object $dateTime = CalendarUtils::toGregorianDate(1395, 2, 18); echo $dateTime->format('Y-m-d'); // Output: 2016-05-07 // Batch conversion $dates = [ [2018, 4, 7], [2019, 3, 21], [2020, 1, 1] ]; foreach ($dates as $date) { $jalali = CalendarUtils::toJalali($date[0], $date[1], $date[2]); echo sprintf("%d/%02d/%02d => %d/%02d/%02d\n", $date[0], $date[1], $date[2], $jalali[0], $jalali[1], $jalali[2] ); } // Output: // 2018/04/07 => 1397/01/18 // 2019/03/21 => 1398/01/01 // 2020/01/01 => 1398/10/11 ``` ### CalendarUtils: Validate Dates Validate Jalali or Gregorian dates using CalendarUtils. ```php format('Y-m-d H:i:s'); // Output: 2016-02-14 15:00:00 // Create Carbon from Jalali format $carbon = CalendarUtils::createCarbonFromFormat('Y/m/d H:i:s', $jalaliString); echo $carbon->toDateTimeString(); // Output: 2016-02-14 15:00:00 // Now you can use Carbon's full API $tomorrow = $carbon->addDay(); echo $tomorrow->toDateString(); // Output: 2016-02-15 // Different format patterns $date1 = CalendarUtils::createCarbonFromFormat('Y-m-d', '1397-03-15'); echo $date1->format('l, F j, Y'); // Output: Wednesday, June 6, 2018 $date2 = CalendarUtils::createDatetimeFromFormat('d/m/Y H:i', '25/11/1394 15:30'); echo $date2->format('Y-m-d H:i'); // Output: 2016-02-14 15:30 // With timezone $tehran = new \DateTimeZone('Asia/Tehran'); $date3 = CalendarUtils::createCarbonFromFormat('Y-m-d', '1397-01-01', $tehran); echo $date3->getTimezone()->getName(); // Output: Asia/Tehran ``` ### CalendarUtils: Convert Numbers Between Persian and Latin Convert numeric digits between Persian (۰-۹) and Latin (0-9) characters. ```php format('Y-m-d'); // Output: 2016-05-08 // Format and convert in one go $timestamp = time(); $formatted = CalendarUtils::strftime('%Y/%m/%d', $timestamp); $persian = CalendarUtils::convertNumbers($formatted); echo $persian; // Output: ۱۴۰۳/۰۸/۲۹ ``` ### CalendarUtils: Configure Month Names Switch between Iranian and Afghan month name conventions. ```php فروردین [1] => اردیبهشت [2] => خرداد [3] => تیر [4] => مرداد [5] => شهریور [6] => مهر [7] => آبان [8] => آذر [9] => دی [10] => بهمن [11] => اسفند ) */ // View all Afghan month names print_r(CalendarUtils::AFGHAN_MONTHS_NAME); /* Output: Array ( [0] => حمل [1] => ثور [2] => جوزا [3] => سرطان [4] => اسد [5] => سنبله [6] => میزان [7] => عقرب [8] => قوس [9] => جدی [10] => دلو [11] => حوت ) */ ``` ## Summary and Integration The morilog/jalali library is essential for any PHP application requiring Jalali calendar support, particularly for Iranian or Afghan markets. Common use cases include e-commerce platforms displaying product release dates, financial applications tracking fiscal year boundaries, content management systems scheduling posts in Persian dates, and HR systems managing employee records with Jalali birth dates. The library excels in handling date-sensitive business logic like calculating holidays, generating reports with Persian dates, and providing localized user interfaces. Integration is straightforward via Composer (`composer require morilog/jalali:3.*`), with PHP 7.0+ and Carbon as the only requirements. The API is designed for fluency and immutability, meaning all date modification methods return new instances rather than mutating existing objects. This makes the library safe for concurrent operations and reduces bugs. Developers can seamlessly convert between Jalali and Gregorian calendars, leverage Carbon's extensive DateTime API through the `toCarbon()` method, format dates using Persian month/day names, and validate dates with built-in boundary checks. The library's performance is optimized through the Borkowski algorithm, making it suitable for high-throughput applications processing thousands of date conversions per second.