Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Cron Expression Laravel Package

carlossosa88/cron-expression

Fork of dragonmantank/cron-expression adding nonstandard seconds support for Fcron-style scheduling. Parse cron strings and macros, check if a schedule is due, and compute next/previous run dates with optional second-level precision control.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require carlossosa88/cron-expression
    

    Add to composer.json under require if not using Composer globally.

  2. First Use Case: Validate if a predefined schedule is due and fetch the next run time:

    use Cron\CronExpression;
    
    $cron = CronExpression::factory('@daily');
    if ($cron->isDue()) {
        echo "Task is due now!";
    }
    echo $cron->getNextRunDate()->format('Y-m-d H:i:s'); // Next run time
    
  3. Key Classes/Methods:

    • CronExpression::factory(): Parse a cron string (e.g., @hourly, 0 * * * * *).
    • isDue(): Check if the current time matches the cron schedule.
    • getNextRunDate(): Get the next occurrence (supports DateTime or DateTimeImmutable).
    • getPreviousRunDate(): Get the last occurrence before a given time.
  4. Where to Look First:


Implementation Patterns

Common Workflows

  1. Task Scheduling in Laravel: Use with Laravel’s schedule:run command or custom cron jobs:

    $schedule = CronExpression::factory('*/5 * * * * *'); // Every 5 seconds
    if ($schedule->isDue()) {
        // Execute task (e.g., sync data, send notifications).
    }
    
  2. Dynamic Cron Validation: Validate user-input cron expressions (e.g., in a settings panel):

    try {
        $cron = CronExpression::factory($userInput);
        if ($cron->isValid()) {
            // Store or use the expression.
        }
    } catch (\InvalidArgumentException $e) {
        // Handle invalid syntax.
    }
    
  3. Timezone-Aware Scheduling: Pass a DateTimeZone to getNextRunDate():

    $tz = new DateTimeZone('America/New_York');
    $nextRun = $cron->getNextRunDate(null, null, false, $tz);
    
  4. Iterative Run Dates: Fetch future runs (e.g., for a calendar or preview):

    $nextRun = $cron->getNextRunDate(null, 3); // 3rd occurrence from now.
    
  5. Seconds Support: Use non-standard cron fields (e.g., for fcron):

    $cron = CronExpression::factory('* * * * * */2'); // Every 2 seconds.
    $cron->isDue(new DateTime(), null, false); // Drop seconds = false.
    

Integration Tips

  • Laravel Task Scheduling: Combine with Laravel’s Artisan::schedule() for declarative cron jobs:

    $schedule->command('backup:run')->cron('0 2 * * *'); // Daily at 2 AM.
    

    Use carlossosa88/cron-expression to validate custom expressions.

  • Queue Workers: Check cron due status in queue jobs to trigger delayed tasks:

    if ($cron->isDue()) {
        dispatch(new ProcessPaymentJob());
    }
    
  • Testing: Mock DateTime for predictable tests:

    $fixedTime = new DateTime('2023-01-01 00:00:00');
    $this->assertTrue($cron->isDue($fixedTime));
    
  • Edge Cases: Handle invalid expressions gracefully:

    try {
        $cron = CronExpression::factory('invalid * * * *');
    } catch (\InvalidArgumentException $e) {
        report($e); // Log or notify admin.
    }
    

Gotchas and Tips

Pitfalls

  1. Seconds Field Quirks:

    • The seconds field (6th position) is non-standard. Ensure dropSeconds = false in isDue():
      $cron->isDue(null, null, false); // Critical for seconds-based cron.
      
    • Default behavior drops seconds, which may cause false negatives for second-level schedules.
  2. Timezone Handling:

    • Omit timezone arguments to use the system default. Explicitly pass a DateTimeZone for consistency:
      $nextRun = $cron->getNextRunDate(null, null, false, new DateTimeZone('UTC'));
      
    • Avoid DST transitions by testing edge cases (e.g., 2023-03-12 01:30:00 in ambiguous timezones).
  3. Leading Zeros:

    • Expressions like 05 * * * * (5 AM) are valid, but 5 * * * * may fail if not parsed correctly. Test with both formats.
  4. Step Validation:

    • Steps larger than field ranges (e.g., */99 in minutes) are allowed but may yield unexpected results. Validate steps explicitly:
      if ($cron->getStep() > 60) {
          throw new \RuntimeException('Invalid step size.');
      }
      
  5. Day-of-Month vs. Day-of-Week Conflicts:

    • Expressions like 15 * * * 1 (15th of the month and Monday) may not work as expected. Use ? or L (last day) for clarity:
      // Last Monday of the month:
      $cron = CronExpression::factory('0 0 L * 1');
      
  6. Immutable DateTime:

    • Prefer DateTimeImmutable for thread safety in concurrent environments:
      $nextRun = $cron->getNextRunDate(DateTimeImmutable::createFromFormat('Y-m-d', '2023-01-01'));
      

Debugging Tips

  1. Error Messages:

    • Parse errors now include human-readable positions (e.g., "Invalid day-of-week at position 5"). Use this to debug malformed expressions.
  2. Logging Run Dates:

    • Log getNextRunDate() outputs to verify schedules:
      \Log::debug('Next run:', ['date' => $nextRun->format('Y-m-d H:i:s')]);
      
  3. Timezone Debugging:

    • Force UTC for debugging:
      $cron->getNextRunDate(null, null, false, new DateTimeZone('UTC'));
      
  4. Unit Testing:

    • Test edge cases like:
      • Leap seconds (if applicable).
      • Month-end dates (e.g., 31 12 * * * in April).
      • Ambiguous timezones (e.g., 2023-10-29 01:30:00 in America/New_York).

Extension Points

  1. Custom Validators: Extend the parser to enforce project-specific rules (e.g., disallow * in hours):

    if (strpos($expression, '* * * * *') !== false) {
        throw new \InvalidArgumentException('Wildcard hours not allowed.');
    }
    
  2. Alternative Time Fields: Override CronExpression to support custom fields (e.g., quarters):

    class QuarterCronExpression extends CronExpression {
        public static function factory($expression) {
            // Add quarter logic (e.g., 'Q1' -> '1-3').
        }
    }
    
  3. Performance Optimization: Cache parsed expressions for frequent checks:

    static $cache = [];
    if (!isset($cache[$expression])) {
        $cache[$expression] = CronExpression::factory($expression);
    }
    
  4. Localization: Translate error messages for non-English environments:

    $translator = app('translator');
    $error = $translator->get('cron.invalid_expression', ['position' => 3]);
    

Configuration Quirks

  1. Default Timezone: The library uses the system default if no timezone is provided. Set it explicitly in your app:

    date_default_timezone_set('UTC');
    
  2. Max Iterations: getNextRunDate() has no hardcoded limit, but very large steps (e.g., */10000) may cause performance issues. Add a safeguard:

    if ($step > 1000) {
        throw new \
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
craftcms/url-validator
directorytree/privacy-filter-classifier
directorytree/privacy-filter
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi
splash/scopes
splash/toolkit
testo/output-teamcity
testo/bridge-symfony