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
2
Answers
The scheduler helper methods just build a cron expression, so we can look at the expression from your example to see if it does what you expect. Running:
return
"0 10 1 * 2"
which means run at 10am on the 1st of every month and and Tuesdays. (You can check that, and other expressions, here).Your
when
filter will ensure it does not get run on the 1st of the month, but there is no need for themonthly()
call as without it you get the expression"0 10 * * 2"
which means at 10am on Tuesdays.As for testing if it works you can mock the current time for
Carbon
. The following code will check if the command will run at the specified time:If you wanted to run this in a unit test you can get the schedules defined in the kernel with
resolve(IlluminateConsoleSchedulingSchedule::class)->events();
you can then find the event in question and call theisDue
andfiltersPass
method to see if it would run at the specified time. Laravel also offers some helper methods in the baseTestCase
class for mocking out the time: https://laravel.com/docs/9.x/mocking#interacting-with-timeFrom laravel documentation:
If I do it with the code you provided this is what I get
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 inNext due
column it shows it will run next Tuesday2023-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 withwhen()
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:
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