CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due

Overview

PHP Cron Expression Parser

Latest Stable Version Total Downloads Build Status StyleCI

The PHP cron expression parser can parse a CRON expression, determine if it is due to run, calculate the next run date of the expression, and calculate the previous run date of the expression. You can calculate dates far into the future or past by skipping n number of matching dates.

The parser can handle increments of ranges (e.g. */12, 2-59/3), intervals (e.g. 0-9), lists (e.g. 1,2,3), W to find the nearest weekday for a given day of the month, L to find the last day of the month, L to find the last given weekday of a month, and hash (#) to find the nth weekday of a given month.

More information about this fork can be found in the blog post here. tl;dr - v2.0.0 is a major breaking change, and @dragonmantank can better take care of the project in a separate fork.

Installing

Add the dependency to your project:

composer require dragonmantank/cron-expression

Usage



require_once '/vendor/autoload.php';

// Works with predefined scheduling definitions
$cron = new Cron\CronExpression('@daily');
$cron->isDue();
echo $cron->getNextRunDate()->format('Y-m-d H:i:s');
echo $cron->getPreviousRunDate()->format('Y-m-d H:i:s');

// Works with complex expressions
$cron = new Cron\CronExpression('3-59/15 6-12 */15 1 2-5');
echo $cron->getNextRunDate()->format('Y-m-d H:i:s');

// Calculate a run date two iterations into the future
$cron = new Cron\CronExpression('@daily');
echo $cron->getNextRunDate(null, 2)->format('Y-m-d H:i:s');

// Calculate a run date relative to a specific time
$cron = new Cron\CronExpression('@monthly');
echo $cron->getNextRunDate('2010-01-12 00:00:00')->format('Y-m-d H:i:s');

CRON Expressions

A CRON expression is a string representing the schedule for a particular command to execute. The parts of a CRON schedule are as follows:

*    *    *    *    *
-    -    -    -    -
|    |    |    |    |
|    |    |    |    |
|    |    |    |    +----- day of week (0 - 7) (Sunday=0 or 7)
|    |    |    +---------- month (1 - 12)
|    |    +--------------- day of month (1 - 31)
|    +-------------------- hour (0 - 23)
+------------------------- min (0 - 59)

This library also supports a few macros:

  • @yearly, @annually - Run once a year, midnight, Jan. 1 - 0 0 1 1 *
  • @monthly - Run once a month, midnight, first of month - 0 0 1 * *
  • @weekly - Run once a week, midnight on Sun - 0 0 * * 0
  • @daily - Run once a day, midnight - 0 0 * * *
  • @hourly - Run once an hour, first minute - 0 * * * *

Requirements

  • PHP 7.1+
  • PHPUnit is required to run the unit tests
  • Composer is required to run the unit tests

Projects that Use cron-expression

Comments
  • RuntimeException: Impossible CRON expression at Date 2022-10-30 on Europe/Berlin Timezone

    RuntimeException: Impossible CRON expression at Date 2022-10-30 on Europe/Berlin Timezone

    At Version Version 3.2.2 I got a RuntimeException when the Date reaches the German time change.

    The following Code works fine:

    $cronExpression = new CronExpression('0 0 1 * *');
    $date = new \DateTime('2022-10-30', new \DateTimeZone("UTC"));
    $cronExpression->getNextRunDate($date)
    

    but if I change the Timezone to Europe/Berlin:

    $cronExpression = new CronExpression('0 0 1 * *');
    $date = new \DateTime('2022-10-30', new \DateTimeZone("Europe/Berlin"));
    $cronExpression->getNextRunDate($date)
    

    I get a RuntimeException: Impossible CRON expression

    Debugging from AbstractField::timezoneSafeModify:

        protected function timezoneSafeModify(DateTimeInterface $dt, string $modification): DateTimeInterface
        {
            $timezone = $dt->getTimezone();
            var_dump($dt);
            $dt = $dt->setTimezone(new \DateTimeZone("UTC"));
            var_dump($dt);
            $dt = $dt->modify($modification);
            $dt = $dt->setTimezone($timezone);
            die(var_dump($dt));
            return $dt;
        }
    
    object(DateTime)[2430]
      public 'date' => string '2022-10-30 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)
    /src/Cron/AbstractField.php:327:
    object(DateTime)[2430]
      public 'date' => string '2022-10-29 22:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'UTC' (length=3)
    /src/Cron/AbstractField.php:330:
    object(DateTime)[2430]
      public 'date' => string '2022-10-30 23:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)
    

    If needed, i can pr a failing Test by tomorrow.

    opened by Apfelfrisch 10
  • Multiple

    Multiple "Day if Month" values including "L" for Last Day of Month

    I love this library. I'm using it to run billing on varying schedules.

    I just ran into a situation where one customer needs an invoice generated from the 1st of the month through the 15th of the month, and from the 15th of the month through the last day of the month. So invoice generation needs to run at the end of the day (for them this is 6:00 PM PST) on the 15 and the last day of the month. My schedule looks like this:

    0 18 L,15 * *

    Unfortunately, it does not look like the "day of the month" supports multiple comma-separated days including "L" for the last day, the way it does numeric days.

    If this is a desired feature, I'm happy to try and tackle it.

    opened by iambrianreich 9
  • Valid cron expression is not accepted

    Valid cron expression is not accepted

    Input

    $expression = '2,17,35,47 5-7,11-13 * * *';

    Expected behaviour

    • CronExpression::isValidExpression($expression) returns true
    • CronExpression::factory($expression) returns a CronExpression instance.

    Referring to crontab.guru this is a valid expression.

    Actual behaviour

    • CronExpression::isValidExpression($expression) returns false
    • CronExpression::factory($expression) throws an InvalidArgumentException with message Invalid CRON field value 5-7,11-13 at position 1

    Identified cause

    Range and list at the same time in the same field are not accepted.

    See https://github.com/dragonmantank/cron-expression/blob/v2.0.0/src/Cron/AbstractField.php#L210


    I would like to provide a PR, if you confirm this is a valid issue.

    I once implemented a cron expression validator myself, which allows this expression. I updated it today to be compatible with the PHPUnit 6+ version.

    bug 
    opened by hollodotme 9
  • Bug:

    Bug: "0-59/59 10 * * *" skips 10:00

    @dragonmantank

    cron schedule: 0-59/59 10 * * *

    $times = [];
    $now = new Carbon();
    
    $cron = CronExpression::factory("0-59/59 10 * * *");
    for ($k = 0 ; $k < 20; $k++){
    	$times[] = $cron->getNextRunDate($now, $k);
    }
    

    There is a bug. 2020-08-20 10:00:00 is missed. The first time is 2020-08-20 10:59:00

    opened by pjebs 8
  • Add PHPCS fixer to project

    Add PHPCS fixer to project

    I think it is useful to have working PHPCS functionality in the project it self, which can now simply be executed using:

    composer phpcs for a dry run composer phpcs-fix for actual fixing.

    For now only applied @PSR2

    opened by holtkamp 8
  • range(): step exceeds the specified range

    range(): step exceeds the specified range

    @dragonmantank I'm using v2 of library:

    $now = new Carbon();
    $cron = CronExpression::factory("41-59/24 5 * * 5");
    for ($k = 0 ; $k < 20; $k++){
    	$t = $cron->getNextRunDate($now, $k);
    	$times[] = $t; // Store next 20 values
    }
    		
    

    Error:

    range(): step exceeds the specified range

    /vendor/dragonmantank/cron-expression/src/Cron/AbstractField.php:148 /vendor/dragonmantank/cron-expression/src/Cron/AbstractField.php:53 /vendor/dragonmantank/cron-expression/src/Cron/MinutesField.php:31 /vendor/dragonmantank/cron-expression/src/Cron/CronExpression.php:362 /vendor/dragonmantank/cron-expression/src/Cron/CronExpression.php:199

    opened by pjebs 7
  • set phpstan to level max and fix issues

    set phpstan to level max and fix issues

    Hello,

    I do:

    • Set PHPStan to level max (ie 8) https://phpstan.org/user-guide/rule-levels.
    • Fix issues throws by PHPStan
    • Add PHPStan to Travis

    It improve DX :rocket:

    opened by oallain 6
  • or/and bug

    or/and bug

    I use the library in a script that monitors cronjobs. Now we have the issue that crond handle the expression in another way than the library predicted.

    The expression is: 30 0 1 * 1

    Crond run on every monday and on every 1th of the month. Crond use OR. The library use AND. So the library thinks cron would only run when the 1th of the month is a monday.

    Is it intentional that the library behaves like this or is it a bug?

    opened by adminc 5
  • Add PHP7.x primitive type hints to method declaration

    Add PHP7.x primitive type hints to method declaration

    The library supports all PHP Versions >=7.0.0 according to the composer.json. That's why this PR adds strict scalar type hints to the methods that are currently defined in the src folder. I added the types according to the DocBlock of each function.

    Feedback is much appreciated :tada:

    opened by legionth 5
  • Change private variables to protected

    Change private variables to protected

    This allows extending classes to access the order for parts the same way as the base class.

    This introduces no breaking changes. Could be introduced as a patch release.

    opened by DerekCresswell 4
  • Human Readable cron String

    Human Readable cron String

    FEATURE request:

    It would be nice to have a method to translate cron string to a human readable format, like prettyCron js library (http://azza-bazoo.github.io/prettycron/). So for example:

    Cron expression: 0 * * * * Human readable: Every hour, on the hour

    Cron expression: 30 * * * 1 Human readable: Every 30th minute past every hour on Mon

    Cron expression: 15,45 9,21 * * * Human readable: 09:15, 09:45, 21:15 and 21:45 every day

    Cron expression: 18,19 7 5 * * Human readable: 07:18 and 07:19 on the 5th of every month

    Cron expression: * * 25 12 * Human readable: Every minute on the 25th in Dec

    Cron expression: 0 * 1,3 * * Human readable: Every hour, on the hour on the 1 and 3rd of every month

    Cron expression: 0 17 * 1,4,7,10 * Human readable: 17:00 every day in Jan, Apr, Jul and Oct

    opened by paianoa 4
  • What is Due() for?

    What is Due() for?

    Might be a stupid question.

    Can someone explain what $blah->Due() is for...... I thought it was to check if a datetime = exactly now? So if it's not 12 am on the 11th of November (eg. before it) it would return false eg. $blah = new Cron\CronExpression( 'blah '); $blah->Due( '2022-11-11 00:00:00' ); but it always returns true

    Do we need to check ourselves if a datetime = exactly now?

    opened by peterlaws 0
  • DayOfMonthField : 'LW' option !?

    DayOfMonthField : 'LW' option !?

    Hi Michael !

    thanks for the repo, great job here !

    I try to find out if I'm doing something wrong, or if the lib just doesn't handle the 'LW' combination option at the DayOfMonthField position.

    like 0 10 LW * * stands for "At 10:00 AM, on the last weekday of the month" but I've got an error "Uncaught Error: Call to a member function format() on bool in /var/www/html/vendor/dragonmantank/cron-expression/src/Cron/DayOfMonthField.php:51"

    could you please confirm if LW works !?

    opened by tomitomas 1
  • #137 Add check for multiple question marks

    #137 Add check for multiple question marks

    This solves #137

    Passing two question marks into a cron expression does not really make sense in my opinion, as you would neither specify the day of month nor the day of week. Wikipedia states

    In some implementations, used instead of '*' for leaving either day-of-month or day-of-week blank.

    I guess, this is the implementation this library is following.

    I've also added a check to validate if there are question marks in parts, where they are not allowed. Maybe there should be an extra step to validate each part against it's allowed values. What do you think about this?

    opened by LeoVie 0
Releases(v3.3.2)
  • v3.3.2(Sep 10, 2022)

    [3.3.2] - 2022-09-19

    Added

    • N/A

    Changed

    • Skip some daylight savings time tests for PHP 8.1 daylight savings time weirdness (#146)

    Fixed

    • Changed string interpolations to work better with PHP 8.2 (#142)
    Source code(tar.gz)
    Source code(zip)
  • v3.3.1(Jan 18, 2022)

    [3.3.1] - 2022-01-18

    Added

    • N/A

    Changed

    • N/A

    Fixed

    • Fixed issue when timezones had no transition, which can occur over very short timespans (#134)
    Source code(tar.gz)
    Source code(zip)
  • v3.3.0(Jan 14, 2022)

    [3.3.0] - 2022-01-13

    Added

    • Added ability to register your own expression aliases (#132)

    Changed

    • Changed how Day of Week and Day of Month resolve when one or the other is * or ?

    Fixed

    • PHPStan should no longer error out
    Source code(tar.gz)
    Source code(zip)
  • v3.2.4(Jan 13, 2022)

  • v3.2.3(Jan 6, 2022)

  • v3.2.2(Jan 5, 2022)

    [3.2.2] - 2022-01-05

    Added

    • N/A

    Changed

    • Marked some methods @internal (#124)

    Fixed

    • Fixed issue with small ranges and large steps that caused an error with range() (#88)
    • Fixed issue where wraparound logic incorrectly considered high bound on range (#89)
    Source code(tar.gz)
    Source code(zip)
  • v3.2.1(Jan 5, 2022)

    [3.2.1] - 2022-01-04

    Added

    • N/A

    Changed

    • Added PHP 8.1 to testing (#125)

    Fixed

    • Allow better mixture of ranges, steps, and lists (#122)
    • Fixed return order when multiple dates are requested and inverted (#121)
    • Better handling over DST (#115)
    • Fixed PHPStan tests (#130)
    Source code(tar.gz)
    Source code(zip)
  • v3.2.0(Jan 5, 2022)

    [3.2.0] - 2022-01-04

    Added

    • Added alias for @midnight (#117)

    Changed

    • Improved testing for instance of field in tests (#105)
    • Optimization for determining multiple run dates (#75)
    • CronExpression properties changed from private to protected (#106)

    Fixed

    • N/A
    Source code(tar.gz)
    Source code(zip)
  • v3.1.0(Nov 24, 2020)

    [3.1.0] - 2020-11-24

    Added

    • Added CronExpression::getParts() method to get parts of the expression as an array (#83)

    Changed

    • Changed to Interfaces for some type hints (#97, #86)
    • Dropped minimum PHP version to 7.2
    • Few syntax changes for phpstan compatibility (#93)

    Fixed

    • N/A

    Deprecated

    • Deprecated CronExpression::factory in favor of the constructor (#56)
    • Deprecated CronExpression::YEAR as a formality, the functionality is already removed (#87)
    Source code(tar.gz)
    Source code(zip)
  • v3.0.2(Oct 13, 2020)

  • v2.3.1(Oct 13, 2020)

  • 3.0.1(Aug 21, 2020)

  • v3.0.0(Mar 25, 2020)

    MAJOR CHANGE - In previous versions of this library, setting both a "Day of Month" and a "Day of Week" would be interpreted as an AND statement, not an OR statement. For example:

    30 0 1 * 1

    would evaluate to "Run 30 minutes after the 0 hour when the Day Of Month is 1 AND a Monday" instead of "Run 30 minutes after the 0 hour on Day Of Month 1 OR a Monday", where the latter is more inline with most cron systems. This means that if your cron expression has both of these fields set, you may see your expression fire more often starting with v3.0.0.

    Added

    • Additional docblocks for IDE and documentation
    • Added phpstan as a development dependency
    • Added a Cron\FieldFactoryInterface to make migrations easier (#38)

    Changed

    • Changed some DI testing during TravisCI runs
    • \Cron\CronExpression::determineTimezone() now checks for \DateTimeInterface instead of just \DateTime
    • Errors with fields now report a more human-understandable error and are 1-based instead of 0-based
    • Better support for \DateTimeImmutable across the library by typehinting for \DateTimeInterface now
    • Literals should now be less case-sensative across the board
    • Changed logic for when both a Day of Week and a Day of Month are supplied to now be an OR statement, not an AND

    Fixed

    • Fixed infinite loop when determining last day of week from literals
    • Fixed bug where single number ranges were allowed (ex: 1/10)
    • Fixed nullable FieldFactory in CronExpression where no factory could be supplied
    • Fixed issue where logic for dropping seconds to 0 could lead to a timezone change
    Source code(tar.gz)
    Source code(zip)
Owner
Chris Tankersley
I am a Senior Developer Advocate for @Vonage. I work primarily in PHP but consider myself a polyglot developer.
Chris Tankersley
Doctrine type mappings for brick/date-time

brick/date-time-doctrine Doctrine type mappings for brick/date-time. Introduction This library provides type mappings to use brick/date-time objects s

Brick 6 Jun 20, 2022
Add jalali(persian) date to akaunting software

Akaunting Jalali Date Akaunting Jalali Date is a free module developed for Akaunting. It automatically converts all dates to jalali date and change gr

Ali Hashemi 4 Dec 29, 2022
A package which provides a monthly calendar with days and events depending on given months and events.

A package which provides a monthly calendar with days and events depending on given months and events. This is where your description should go. Try a

MichaB 6 Nov 1, 2022
Parse, validate, manipulate, and display dates in PHP w/ i18n support. Inspired by moment.js

Support I am a dad now for the last 1,5 years and that clearly shows in being on time with merging PRs or pushing this package further. Time is the bi

Tino Ehrich 944 Dec 21, 2022
Parse and validate crontab expressions in PHP

Standard (V7) compliant crontab expression parser/validator with support for time zones; see "man 5 crontab" for possible expressions.

René Pollesch 42 Dec 14, 2022
This library helps PHP users to convert and using Jalali DateTime format

Easy Jalali for PHP V1.0.0 Jalali calendar converter for Persian users in Iran, Afghanistan and other countries that use Jalali calendar. Very thanks

Majid J 3 Mar 20, 2022
Featured Calendar Maker v1.0 multingual extends the functionalities of the latest version of the FullCalendar (5.3.2), the most popular and completed JavaScript Calendar open source

Featured Calendar Maker v1.0 multingual extends the functionalities of the latest version of the FullCalendar (5.3.2), the most popular and completed JavaScript Calendar open source.

null 21 Oct 5, 2022
A simple PHP API extension for DateTime.

Carbon An international PHP extension for DateTime. http://carbon.nesbot.com <?php use Carbon\Carbon; printf("Right now is %s", Carbon::now()->toDat

Brian Nesbitt 16k Dec 30, 2022
The easy PHP Library for calculating holidays

Introduction Yasumi (Japanese for 'Holiday'「休み」) is the easy PHP library that helps you retrieve the dates and names of holidays and other special cel

AzuyaLabs 926 Dec 29, 2022
The missing PHP 5.3+ calendar management library.

CalendR CalendR is an Object Oriented Calendar management library on top of PHP5.3+ Date objects. You can use it to deal with all your needs about cal

Yohan Giarelli 462 Dec 30, 2022
iCal-creator for PHP - This package offers an abstraction layer for creating iCalendars files

This package offers an abstraction layer for creating iCalendars files. By using this PHP package, you can create *.ics files without the knowledge of the underling format. The output itself will follow RFC 5545 as good as possible.

Markus Poerschke 1k Dec 23, 2022
CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due

The PHP cron expression parser can parse a CRON expression, determine if it is due to run, calculate the next run date of the expression, and calculate the previous run date of the expression. You can calculate dates far into the future or past by skipping n number of matching dates.

Chris Tankersley 4.3k Jan 9, 2023
Smd horizon - Next/previous Textpattern article without restrictions

smd_horizon The existing tags <txp:next_title />, <txp:link_to_next /> and their prev counterparts cease to function when they reach the first/last po

Stef Dawson 1 Oct 22, 2019
Cron expression generator built on php8

The most powerful and extendable tool for Cron expression generation Cron expression generator is a beautiful tool for PHP applications. Of course, th

Pavel Buchnev 46 Nov 27, 2022
Date/Time Picker widget for Yii2 framework Based on Eonasdan's Bootstrap 3 Date/Time Picker

Yii2 Date/Time Picker Widget Date/Time Picker widget for Yii2 framework Based on Eonasdan's Bootstrap 3 Date/Time Picker Demo Since this is a part of

Yevhen Terentiev 8 Mar 14, 2022
A cosmetics plugin that is highly incomplete & unoptimized (as it was roughly coded in a small period of time due to IRL issues), that I got scammed for,

CosmeticsPlus A cosmetics plugin that is highly incomplete and; unoptimized (as it was roughly coded in a small period of time due to IRL issues), tha

Seekherr 5 Feb 7, 2022
Analyze content to determine the appropriate Internet media type

Canal Content analysis for the purpose of determining Internet media types. Requirements PHP 5.3+ Installation Through Composer as dflydev/canal. Usag

dflydev 34 Feb 11, 2022
Determine the geographical location of website visitors based on their IP addresses.

GeoIP for Laravel Determine the geographical location and currency of website visitors based on their IP addresses. GeoIP for Laravel on Packagist Geo

Daniel Stainback 1.9k Jan 6, 2023
A Zabbix 5.4 module to group items under Monitoring -> Latest data per Tag as it used to be with Application grouping in previous versions of Zabbix

zabbix-module-latest-data Written according to Zabbix official documentation https://www.zabbix.com/documentation/current/manual/modules A Zabbix 5.4

BGmot 18 Dec 6, 2022
Determine the geographical location of website visitors based on their IP addresses.

Determine the geographical location and currency of website visitors based on their IP addresses.

Daniel Stainback 1.9k Dec 22, 2022