I've been tasked with setting up a schedule for a command to run on the 2nd Tuesday of each month, at 10am. I haven't been able to find any real way to test when the command will actually run.
This is what I have at the moment:
$schedule->command('foo')
->monthly()
->tuesdays()
->at('10:00')
->when(function () {
return Carbon::now()->weekOfMonth == 2;
})
->withoutOverlapping()
->appendOutputTo(storage_path($logPath));
Am I correct in thinking that the 'when' closure is evaluated only when the command is actually scheduled to run? Do I even need the 'monthly' condition? And most importantly: will this only run on the 2nd Tuesday of each month, at 10am?
Also, is there a way I could test this schedule without running the command (e.g. by passing in a date and getting a true/false if it'll run)?
Thanks
From laravel documentation:
If you would like to view an overview of your scheduled tasks and the next time they are scheduled to run, you may use the schedule:list Artisan command
php artisan schedule:list
If I do it with the code you provided this is what I get
+------------------------------------+------------+-------------+----------------------------+
| Command | Interval | Description | Next Due |
+------------------------------------+------------+-------------+----------------------------+
| '/usr/local/bin/php' 'artisan' foo | 0 10 1 * 2 | | 2023-01-17 10:00:00 +00:00 |
+------------------------------------+------------+-------------+----------------------------+
And if we take the listed interval
and put it into a tool like cronhub's crontab expression generator, it will tell you it will run At 10:00 AM, on day 1 of the month, and on Tuesday. This means that it will only run every 1st of the month, but only if it falls on Tuesday. Although in Next due
column it shows it will run next Tuesday 2023-01-17 10:00:00 +00:00
I think this is an error in Laravel.
So, what you actually want to do is remove the ->monthly()
interval so that the command runs every Tuesday, then a callback function you provided with when()
is executed and if it returns true it will proceed, meaning your command will only run every second Tuesday in a month.
Or, you could acomplish the same thing by using the cron expressions directly, like this:
$schedule->command('foo')
->cron('0 10 * * TUE#2')
->withoutOverlapping();
->appendOutputTo(storage_path($logPath));
If I run php artisan schedule:list
today, it will correctly list the next due date to be February 14th 2023, which is second Tuesday is that month
+------------------------------------+----------------+-------------+----------------------------+
| Command | Interval | Description | Next Due |
+------------------------------------+----------------+-------------+----------------------------+
| '/usr/local/bin/php' 'artisan' foo | 0 10 * * TUE#2 | | 2023-02-14 10:00:00 +00:00 |
+------------------------------------+----------------+-------------+----------------------------+