Skip to main content

dragonmantank/cron-expression - PHP cron parser guide

At 04:30, every day.

Next runs · UTC

Calendar

September 2026 · UTC

0 runs

About this tool

dragonmantank/cron-expression is the de-facto cron parser for PHP - used by Laravel scheduling, Symfony Scheduler, and most modern PHP scheduling libraries. It supports the standard 5-field syntax plus the L, W, ? and # modifiers used by Quartz / AWS EventBridge dialects.
For last-day-of-month with this library see the dedicated guide.

Using cron-expression directly

php
<?php
require 'vendor/autoload.php';
use Cron\CronExpression;


$cron = new CronExpression('0 9 * * 1-5');


// Is the cron due right now?
if ($cron->isDue()) {
    runJob();
}


// When does it fire next?
$next = $cron->getNextRunDate();
echo $next->format('Y-m-d H:i'); // e.g. "2026-04-27 09:00"


// Multi-step preview
foreach ($cron->getMultipleRunDates(5) as $date) {
    echo $date->format('Y-m-d H:i') . "\n";
}

Laravel scheduling on top of cron-expression

php
<?php
// app/Console/Kernel.php


protected function schedule(Schedule $schedule)
{
    $schedule->command('reports:daily')
             ->dailyAt('09:00')
             ->timezone('America/New_York');


    $schedule->command('billing:close-month')
             ->lastDayOfMonth();   // → '0 0 L * *'


    $schedule->command('cleanup:hourly')
             ->cron('15 * * * *'); // raw cron expression
}

Laravel's named helpers compile to cron expressions under the hood. lastDayOfMonth() uses L, which the underlying parser supports.

Symfony Scheduler on top of cron-expression

php
<?php


use Symfony\Component\Scheduler\Attribute\AsSchedule;
use Symfony\Component\Scheduler\Schedule;
use Symfony\Component\Scheduler\ScheduleProviderInterface;
use Symfony\Component\Scheduler\Trigger\CronExpressionTrigger;


#[AsSchedule('default')]
final class DefaultSchedule implements ScheduleProviderInterface
{
    public function getSchedule(): Schedule
    {
        return (new Schedule())
            ->add(
                CronExpressionTrigger::fromSpec('0 9 * * 1-5')
                    ->message(new MorningDigestMessage()),
                CronExpressionTrigger::fromSpec('0 0 L * *')
                    ->message(new MonthEndMessage()),
            );
    }
}

Modifier support

  • L - last day of month or last weekday-of-month.
  • LW - last weekday of the month.
  • W - nearest weekday to the given day-of-month.
  • ? - no specific value (compatibility token).
  • # - Nth weekday of month, e.g. 2#1 = first Monday.

Pitfalls

  • Default timezone - `getNextRunDate()` uses the PHP default timezone. Set it via date_default_timezone_set or pass an explicit DateTimeZone.
  • Laravel scheduler precision - runs every minute via a single OS cron, so jobs fire somewhere in their target minute (not at the exact second).
  • Backward-compat with v1 - older versions used CronExpression::factory(). New code uses new CronExpression(...).

Examples

  • 0 18 * * *
    Every day at 18:00
  • 0 */5 * * *
    Every 5 hours
  • 0 18 * * 1-5
    Weekdays at 18:00
  • 0 0 1 * *
    Once a month, on the 1st at midnight

Cheatsheet

Full cheatsheet
FieldReq.RangeWildcards
minuteyes0-59, - * /
houryes0-23, - * /
day of monthyes1-31, - * /
monthyes1-12 or JAN-DEC, - * /
day of weekyes0-6 or names; some dialects use 0-7 or 1-7 — see Dialects, - * /

Frequently asked questions

Syntax

How many fields does dragonmantank/cron-expression use?

It uses the standard five-field order: minute, hour, day of month, month, and day of week. It does not add a leading seconds field.

Which extended cron operators does dragonmantank support?

dragonmantank/cron-expression supports L, W, #, and ? in their compatible day-field positions, in addition to lists, ranges, wildcards, and steps.

Does dragonmantank support @daily and other aliases?

Yes. The parser supports @yearly, @annually, @monthly, @weekly, @daily, @midnight, and @hourly aliases.

Timing

What's the difference between isDue() and getNextRunDate()?

isDue() checks whether the cron should fire right now (matches the current minute). getNextRunDate() returns the next time the cron will fire. Use isDue() inside a per-minute scheduler loop; use getNextRunDate() when computing future schedules.

Days and months

How are day-of-month and day-of-week combined by dragonmantank?

dragonmantank follows Vixie cron's OR semantics when both day fields are restricted. A date is due when either restricted day field matches.

How do I express the last day of a month in dragonmantank?

Use 0 0 L * * for midnight on the last calendar day. The L value automatically accounts for 28-, 29-, 30-, and 31-day months.

PHP last-day guide
How do I express an nth weekday with dragonmantank?

Use # in the day-of-week field, such as 1#1 for the first Monday under the library's weekday numbering. Preview dates before deploying numeric weekday expressions.

Timezones and DST

Which timezone does dragonmantank use?

Date calculations use the timezone supplied to the method or PHP's configured default. Pass an explicit timezone when application and server defaults may differ.

Can DST change dragonmantank's next run date?

Yes. Calendar schedules are resolved in a timezone, so missing and repeated local times can affect the resulting occurrence. Use UTC when wall-clock behavior is unnecessary.

Platforms

What PHP versions does dragonmantank/cron-expression support?

Current dragonmantank/cron-expression 3.x requires PHP 8.2 or newer. Check the package's version constraints before selecting an older release for an older PHP runtime.

Is this what Laravel uses for its scheduler?

Yes. Laravel's task scheduler is built on top of dragonmantank/cron-expression. The named helpers (->daily(), ->weekdays(), ->lastDayOfMonth()) compile down to cron strings the parser handles.

Does Symfony Scheduler also use this?

Symfony Scheduler 6.3+ uses cron-expression internally. The CronExpressionTrigger class wraps a cron string and resolves the next run time using dragonmantank's parser.

Debugging

How can I preview several dragonmantank run dates?

Call getMultipleRunDates(count) on a CronExpression. Reviewing several dates is especially useful for month-end, weekday, and DST-sensitive schedules.

How often must Laravel evaluate dragonmantank schedules?

Laravel normally runs schedule:run every minute so isDue() can dispatch matching entries. A missing or delayed outer scheduler prevents due tasks from starting.

Crontap

Can Crontap run a dragonmantank cron schedule?

Yes. You can run a compatible HTTP endpoint on Crontap; confirm dialect-specific operators before copying an expression from the PHP parser.

Write the cron here. Run it on Crontap.

Point Crontap at any URL. Pick any cron. Done.

WordPress, Shopify, Railway, Cloud Run, Vercel, HubSpot, Ghost, your own box. If it answers HTTP, Crontap can drive it on a clock you can read, in the timezone that actually matters, and page you when something breaks.

1.9k+ teams · Free forever tier · No credit card