Search by

erfanvahabpour / laravel-jalali-schedule

ErfanVahabpour

Seamless Jalali (Solar Hijri) scheduling macros for Laravel tasks and console commands.

Package info

github.com/ErfanVahabpour/laravel-jalali-schedule

pkg:composer/erfanvahabpour/laravel-jalali-schedule

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-10-09 13:06 UTC

This package is auto-updated.

Last update: 2026-10-09 13:16:08 UTC


README

Latest Version on Packagist Total Downloads Software License PHP Version Laravel Version

Fluent Jalali (Solar Hijri / هجری شمسی) scheduling macros for Laravel tasks and console commands.

🇮🇷 برای مشاهده مستندات به زبان فارسی کلیک کنید (Persian Documentation)

The Problem

Standard Cron expressions only understand Gregorian calendar fields (minute hour day-of-month month day-of-week). As a result, native Laravel scheduler methods like monthly(), monthlyOn(), quarterly(), and yearly() only trigger on Gregorian dates.

Before this package, scheduling tasks for Iranian business cycles (such as payroll on the 1st of each Shamsi month or VAT reports every season) required verbose, repetitive closures:

// ❌ The cumbersome way (Before)
Schedule::command('payroll:process')
    ->dailyAt('00:15')
    ->when(fn () => verta()->day === 1)
    ->withoutOverlapping()
    ->onOneServer();

With Laravel Jalali Schedule, it becomes expressive, clean, and fluent:

// ✅ The clean way (After)
Schedule::command('payroll:process')
    ->jalaliMonthlyOn(1, '00:15')
    ->withoutOverlapping()
    ->onOneServer();

Features

  • 📅 Fluent Monthly Scheduling: Run tasks on the 1st, 15th, specific days, or dynamically on the last day of each Jalali month (handling 29, 30, and 31-day months seamlessly).
  • 🍂 Iranian Seasons & Quarters: Easily schedule quarterly reports on the 1st of Farvardin (Bahar), Tir (Tabestan), Mehr (Paeez), and Dey (Zemestan).
  • 🌱 Nowruz & Annual Tasks: Run jobs on Farvardin 1st or custom Jalali month and day.
  • 🏢 Iranian Work Week Helpers: Built-in macros for Saturday through Wednesday (iranWeekdays), Thursday and Friday (iranWeekends), and individual days (shanbeh, jomeh).
  • ⚡ Zero Configuration: Automatically registered via Laravel Package Discovery.
  • 🧪 Time Travel & Test Friendly: Uses Date::now($tz) so Carbon::setTestNow() and $this->travelTo() work seamlessly in tests.
  • 💡 PhpStorm Autocomplete: Includes IDE helper stubs so you get zero "Method not found" warnings and full parameter hinting.

Installation

Install the package via Composer:

composer require erfanvahabpour/laravel-jalali-schedule

The service provider is automatically registered via Laravel's package discovery.

Optional: PhpStorm Autocomplete Support

To enable autocompletion and prevent PhpStorm from reporting "Method not found in Event", publish the IDE helper stub:

php artisan vendor:publish --tag=jalali-schedule-ide-helper

This publishes _ide_helper_jalali_schedule.php to your application's root directory, which PhpStorm indexes immediately.

Usage & API Reference

1. Monthly Scheduling

use Illuminate\Support\Facades\Schedule;

// Run on the 1st day of every Jalali month at 00:00 (or custom time)
Schedule::command('reports:monthly')->jalaliMonthly('01:00');

// Run on a specific day of every Jalali month at a given time
Schedule::command('attendance:reconcile')->jalaliMonthlyOn(1, '00:15');

// Run twice a month (defaults to 1st and 16th, or specify custom days)
Schedule::command('payroll:advances')->jalaliTwiceMonthly(1, 15, '03:00');

// Run on the last day of the Jalali month (dynamically handles 31, 30, or 29/30 leap Esfand)
Schedule::command('finance:close-books')->jalaliLastDayOfMonth('23:50');

// Run on specific days of the month (e.g. 5th, 15th, and 25th)
Schedule::command('reminders:send')->jalaliDaysOfMonth([5, 15, 25], '09:00');

2. Quarterly & Yearly Scheduling

In the Jalali calendar, quarters represent the four Iranian seasons:

  • Q1 (Bahar): Months 1, 2, 3 (Farvardin, Ordibehesht, Khordad)
  • Q2 (Tabestan): Months 4, 5, 6 (Tir, Mordad, Shahrivar)
  • Q3 (Paeez): Months 7, 8, 9 (Mehr, Aban, Azar)
  • Q4 (Zemestan): Months 10, 11, 12 (Dey, Bahman, Esfand)
// Run on the 1st of each Jalali quarter (Farvardin 1, Tir 1, Mehr 1, Dey 1)
Schedule::command('tax:seasonal-vat')->jalaliQuarterly('02:00');

// Run on a specific day of the first month of each quarter (e.g. Day 15)
Schedule::command('audit:quarterly')->jalaliQuarterlyOn(15, '04:00');

// Run annually on Nowruz (Farvardin 1st)
Schedule::command('greeting:nowruz')->jalaliYearly('08:00');

// Run annually on a specific Jalali month and day (e.g. Esfand 29th)
Schedule::command('fiscal:year-end-closing')->jalaliYearlyOn(12, 29, '23:00');

3. Condition-Only Filters (For Custom Timing)

If you have a task with a non-daily frequency (such as hourly() or twiceDaily()) and want to restrict it to specific Jalali dates:

// Run hourly, but only on the 1st day of the Jalali month
Schedule::command('sync:final-attendance')
    ->hourly()
    ->whenJalaliDay(1);

// Run every 2 hours, but only on the last day of the Jalali month
Schedule::command('monitor:closing-activity')
    ->everyTwoHours()
    ->whenJalaliLastDayOfMonth();

// Run every minute during the month of Esfand (Month 12)
Schedule::command('countdown:nowruz')
    ->everyMinute()
    ->whenJalaliMonth(12);

4. Iranian Working Week & Weekends

Standard Laravel methods weekdays() (Mon–Fri) and weekends() (Sat–Sun) correspond to Western business calendars. This package provides Iranian equivalents:

// Saturday through Wednesday (Shanbeh to Chaharshanbeh)
Schedule::command('office:morning-sync')->iranWeekdays('07:30');

// Thursday and Friday (Panjshanbeh and Jomeh)
Schedule::command('backup:weekend-database')->iranWeekends('12:00');

// Specific Iranian day of the week
Schedule::command('digest:weekly')->shanbeh('08:00'); // Saturday
Schedule::command('cleanup:weekly')->jomeh('23:00');   // Friday

Method Reference Summary

Method Parameters Description
jalaliMonthly string $time = '0:0' 1st of every Jalali month
jalaliMonthlyOn int $day = 1, string $time = '0:0' Specific day of every Jalali month
jalaliTwiceMonthly int $first = 1, int $second = 16, string $time = '0:0' Twice per Jalali month
jalaliLastDayOfMonth string $time = '0:0' Last day of the Jalali month (dynamic 29/30/31)
jalaliDaysOfMonth array|int ...$days, string $time = '0:0' Specific days of the Jalali month
jalaliQuarterly string $time = '0:0' 1st day of each Jalali quarter (Farvardin 1, Tir 1, Mehr 1, Dey 1)
jalaliQuarterlyOn int $day = 1, string $time = '0:0' Specific day of the 1st month of each quarter
jalaliYearly string $time = '0:0' Annually on Nowruz (Farvardin 1st)
jalaliYearlyOn int $month = 1, int $day = 1, string $time = '0:0' Annually on specific Jalali month & day
whenJalaliDay int|array ...$days Filter closure for custom intervals
whenJalaliLastDayOfMonth - Filter closure for the last day of the month
whenJalaliMonth int|array ...$months Filter closure for specific month(s)
iranWeekdays string $time = '0:0' Saturday through Wednesday (6,0,1,2,3)
iranWeekends string $time = '0:0' Thursday and Friday (4,5)
shanbeh string $time = '0:0' Saturdays
jomeh string $time = '0:0' Fridays

Testing

Run tests with PHPUnit:

composer test

Changelog

Please see CHANGELOG.md for details on recent updates.

License

The MIT License (MIT). Please see License File for more information.

Author

Developed by Erfan Vahabpour