Giter Site home page Giter Site logo

jmrozanec / cron-utils Goto Github PK

View Code? Open in Web Editor NEW
1.1K 33.0 263.0 2.51 MB

Cron utils for parsing, validations and human readable descriptions as well as date/time interoperability.

Home Page: http://cron-utils.com

License: Apache License 2.0

Java 100.00%
cron-libraries cron-definitions cron-expression cron-utils java cron crontab quartz cron4j hacktoberfest

cron-utils's Introduction

cron-utils

We define crons. And support them.

cron-utils is a Java library to define, parse, validate, migrate crons as well as get human readable descriptions for them. The project follows the Semantic Versioning Convention, provides OSGi metadata and uses Apache 2.0 license.

Gitter Chat Build Status Quality Gate

Download

cron-utils is available on Maven central repository.

<dependency>
    <groupId>com.cronutils</groupId>
    <artifactId>cron-utils</artifactId>
    <version>9.2.0</version>
</dependency>

For Android developers, cron-utils 7.0.0 assumes Android 26+. For earlier Android versions consider using cron-utils 6.0.6. If using ScheduleExpression from Java EE, this should be provided as a runtime dependency.

Current development

We are currently working to update the codebase towards JDK 16, to ensure will be fully compatible with JDK 17 when released.

Now we are developing a new generation of cron-descriptors using neural-translation! Any kind of contributions are welcome: from help with dataset generation to machine learning models training and utilities to load them! If interested, please follow issue #3

Features

  • Create arbitrary cron expressions: you can define your own cron format! Supported fields are: second, minute, hour, day of month, month, day of week, year.
  • You can flag last field as optional!
  • Supports all cron special characters: * / , -
    • Non-standard characters L, W, LW, '?' and # are supported as well!
  • Supports cron extensions / nicknames: @yearly, @annually, @monthly, @weekly, @daily, @midnight, @hourly, @reboot
  • Print to locale specific human readable format (Chinese, English, German, Greek, Indonesian, Japanese, Korean, Polish, Romanian, Spanish, Swahili, and Turkish are fully supported. Dutch, French, Italian, Portuguese and Russian have basic support).
  • Parse and Description process are decoupled: parse once and operate with the result!
  • Build cron expressions using CronBuilder:
    • no need to remember fields and constraints for each cron provider.
    • crons become decoupled from cron provider: anytime you can export to another format.
  • Check if cron expressions are equivalent.
  • Squash multiple cron expressions into a single one!
  • Validate if cron string expressions match a cron definition.
  • Convert crons between different cron definitions: if you need to migrate expressions, CronMapper may help you!
  • Pre-defined definitions for the following cron libraries are provided:
  • Obtain last/next execution time as well as time from last execution/time to next execution.
  • Compute the number of executions between two days, and compare crons based on their execution frequencies.
  • Obtain weekdays count between two dates, considering different weekend policies as well as holidays.
  • Need to map constants between different cron/time libraries? Use ConstantsMapper.

Usage Examples

Below we present some examples. You can find this and others in a sample repo we created to showcase cron-utils libraries!

Build cron definitions

// Define your own cron: arbitrary fields are allowed and last field can be optional
CronDefinition cronDefinition =
    CronDefinitionBuilder.defineCron()
        .withSeconds().and()
        .withMinutes().and()
        .withHours().and()
        .withDayOfMonth()
            .supportsHash().supportsL().supportsW().and()
        .withMonth().and()
        .withDayOfWeek()
            .withIntMapping(7, 0) //we support non-standard non-zero-based numbers!
            .supportsHash().supportsL().supportsW().and()
        .withYear().optional().and()
        .instance();

// or get a predefined instance
cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(QUARTZ);

Build a cron expression

// Create a cron expression. CronMigrator will ensure you remain cron provider agnostic
import static com.cronutils.model.field.expression.FieldExpressionFactory.*;

Cron cron = CronBuilder.cron(CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ))
    .withYear(always())
    .withDoM(between(SpecialChar.L, 3))
    .withMonth(always())
    .withDoW(questionMark())
    .withHour(always())
    .withMinute(always())
    .withSecond(on(0))
    .instance();
// Obtain the string expression
String cronAsString = cron.asString(); // 0 * * L-3 * ? *

Parse

// Create a parser based on provided definition
CronParser parser = new CronParser(cronDefinition);
Cron quartzCron = parser.parse("0 23 * ? * 1-5 *");

... even multi-cron expressions! How about squashing multiple crons into a single line? Instead of writing 0 0 9 * * ? *, 0 0 10 * * ? *, 0 30 11 * * ? * and 0 0 12 * * ? * we can wrap it into 0 0|0|30|0 9|10|11|12 * * ? *

Describe

// Create a descriptor for a specific Locale
CronDescriptor descriptor = CronDescriptor.instance(Locale.UK);

// Parse some expression and ask descriptor for description
String description = descriptor.describe(parser.parse("*/45 * * * * ?"));
// Description will be: "every 45 seconds"

description = descriptor.describe(quartzCron);
// Description will be: "every hour at minute 23 every day between Monday and Friday"
// which is the same description we get for the cron below:
descriptor.describe(parser.parse("0 23 * ? * MON-FRI *"));

Migrate

// Migration between cron libraries has never been so easy!
// Turn cron expressions into another format by using CronMapper:
CronMapper cronMapper = CronMapper.fromQuartzToCron4j();

Cron cron4jCron = cronMapper.map(quartzCron);
// and to get a String representation of it, we can use
cron4jCron.asString();//will return: 23 * * * 1-5

Validate

cron4jCron.validate()

Calculate time from/to execution

// Get date for last execution
ZonedDateTime now = ZonedDateTime.now();
ExecutionTime executionTime = ExecutionTime.forCron(parser.parse("* * * * * ? *"));
ZonedDateTime lastExecution = executionTime.lastExecution(now);

// Get date for next execution
ZonedDateTime nextExecution = executionTime.nextExecution(now);

// Time from last execution
Duration timeFromLastExecution = executionTime.timeFromLastExecution(now);

// Time to next execution
Duration timeToNextExecution = executionTime.timeToNextExecution(now);

Map constants between libraries

// Map day of week value from Quartz to JodaTime
int jodatimeDayOfWeek =
        ConstantsMapper.weekDayMapping(
                ConstantsMapper.QUARTZ_WEEK_DAY,
                ConstantsMapper.JODATIME_WEEK_DAY
        );

Date and time formatting for humans!

Use htime - Human readable datetime formatting for Java! Despite this functionality is not bundled in the same jar, is a cron-utils project you may find useful.

// You no longer need to remember "YYYY-MM-dd KK a" patterns.
DateTimeFormatter formatter = 
	    HDateTimeFormatBuilder
		    .getInstance()
		    .forJodaTime()
		    .getFormatter(Locale.US)
		    .forPattern("June 9, 2011");
String formattedDateTime = formatter.print(lastExecution);
// formattedDateTime will be lastExecution in "dayOfWeek, Month day, Year" format

cron-utils CLI

We provide a simple CLI interface to use cron-utils right from console, without writing a new project! The CLI is a satellite project, available at cron-utils-cli

  • Usage: java -jar cron-utils.jar com.cronutils.cli.CronUtilsCLI --validate -f [CRON4J|QUARTZ|UNIX] -e '<cron expression>'

  • Example: java -jar cron-utils.jar com.cronutils.cli.CronUtilsCLI --validate -f UNIX -e '* 1 * * *'

If you want a standalone jar without requiring the 'cp', build an uber jar with :

mvn assembly:assembly -DdescriptorId=jar-with-dependencies

Then, launch cli-utils (built in the target directory) with :

java -jar cron-utils-<version>-jar-with-dependencies.jar com.cronutils.cli.CronUtilsCLI --validate -f [CRON4J|QUARTZ|UNIX] -e '<cron expression>'`

Contribute & Support!

Contributions are welcome! You can contribute by

  • starring this repo!
  • requesting or adding new features. Check our roadmap!
  • enhancing existing code: ex.: provide more accurate description cases
  • testing
  • enhancing documentation
  • providing translations to support new locales
  • bringing suggestions and reporting bugs
  • spreading the word
  • telling us how you use it! We look forward to list you at our wiki!

Check our page! For stats about the project, you can visit our OpenHUB profile.

Other cron-utils projects

You are welcome to visit and use the following cron-utils projects:

  • htime: A Java library to make it easy for humans format a date. You no longer need to remember date time formatting chars: just write an example, and you will get the appropriate formatter.
  • cron-utils-spring: A Java library to describe cron expressions in human readable language at Spring framework, using cron-utils.
  • cron-utils-cli: cron-utils features made available through a CLI.
  • cron-utils-sisyphus: A Scala scheduler that supports multiple cron notations.
  • cron-utils-scheduler: A Java job scheduler based on cron-utils library.

cron-utils's People

Contributors

albertotn avatar albuhuba avatar alenkacz avatar curioustechizen avatar dependabot[bot] avatar emsyzz avatar eyal-shalev avatar haarolean avatar indeedsi avatar j0b10 avatar jgoldhammer avatar jmrozanec avatar jpotts2 avatar kensipe avatar kostapc avatar lodenrogue avatar lpbak avatar maxmorozov avatar mccartney avatar meincs avatar minidmnv avatar mureinik avatar nielsdoucet avatar pangyikhei avatar pkoenig10 avatar primael avatar rhanton avatar skyline75489 avatar svenssonweb avatar thachhoang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cron-utils's Issues

L option for Quartz Parser is not working

Hi

When I try t use the Quartz Parser for below expression, I got error

expression:

0 0/10 22 L * ?

Error message

Exception in thread "main" java.lang.IllegalArgumentException: Number 0 out of range [1,31]
at com.cronutils.model.field.constraint.FieldConstraints.validateInRange(FieldConstraints.java:111)
at com.cronutils.model.field.expression.FieldExpression.validate(FieldExpression.java:48)
at com.cronutils.model.field.expression.On.<init>(On.java:52)
at com.cronutils.parser.field.FieldParser.parseOn(FieldParser.java:136)
at com.cronutils.parser.field.FieldParser.parse(FieldParser.java:51)
at com.cronutils.parser.field.CronParserField.parse(CronParserField.java:68)
at com.cronutils.parser.CronParser.parse(CronParser.java:88)

I believe the issue is FieldParser/parseOn method. It set 0 for this fileds IntegerFieldValue on On class and it raises exception because DAY_OF_MONTH should be between 1-31

    if (exp.contains("L")) {
        specialChar = new SpecialCharFieldValue(SpecialChar.L);
        exp = exp.replace("L", "");
        if ("".equals(exp)) {
            expression = "0";//to avoid a NumberFormatException
        } else {
            expression = exp;
        }
    }

NullPointerException with custom cronExpression without DaysOfMonth

Hi,

today I've played around with cron-utils but all my efforts to get it working as I expected failed....

I've tried to create a custom expression that contains just fields for "minute", "hour" and "dayOfWeek", so I can configure expressions like "every saturday and sunday at 18:00". But all I've got is a NPE. Her is what I've done:

  final CronDefinition cronDefinition =
          CronDefinitionBuilder.defineCron()
              .withMinutes().and()
              .withHours().and()
              .withDayOfWeek().and()
              .instance();


  // every monday at 18:00
  final String cronExpression = "0 18 1";

  final CronValidator validator = new CronValidator(cronDefinition);
  System.out.println("isValid:" + validator.isValid(cronExpression));

  final CronParser parser = new CronParser(cronDefinition);
  final Cron cron = parser.parse(cronExpression);

  final ExecutionTime executionTime = ExecutionTime.forCron(cron);

The last line throws an NPE:

Exception in thread "main" java.lang.NullPointerException
at com.cronutils.model.time.ExecutionTimeBuilder.validate(ExecutionTimeBuilder.java:114)
at com.cronutils.model.time.ExecutionTimeBuilder.forDaysOfMonthMatching(ExecutionTimeBuilder.java:100)
at com.cronutils.model.time.ExecutionTime.forCron(ExecutionTime.java:81)
at mk.test.Main.testCronUtilsOwnDefinition(Main.java:49)
at mk.test.Main.main(Main.java:21)

Is it a bug or did I make a mistake?

Thank you, Michael

cron mapping not working accurately where '?' symbol is supported.

I'm trying to translate a UNIX cron expression to Quartz cron expression. This is how I'm trying to do so (Groovy code):

Cron unixCron = new CronParser(new CronDefinitionBuilder().instanceDefinitionFor(CronType.UNIX)).parse('* * * * *')
CronMapper cronMapper = new CronMapper(CronDefinitionBuilder.instanceDefinitionFor(CronType.UNIX), CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ))
Cron quartzCron = cronMapper.map(unixCron)
String quartzCronExpression = quartzCron.asString()

However, * * * * * gets translated to 0 * * * * * * which is not valid (I think it should be 0 * * * * * ?). Is this a bug (or unsupported feature) or is my translation code wrong?

Thanks for your help.

Getting extra execution times after updating to v3.1.2

Test File : https://gist.github.com/rtennantventuretech/3e201c854b3546bed2e7

v3.1.1

Test output

TEST1 - expecting 0 instants
instants.size() == 0
instants: []

TEST2 - expecting 12 instants
instants.size() == 12
instants: [1970-01-31T18:00:00.000Z, 1970-02-28T18:00:00.000Z, 1970-03-31T18:00:00.000Z, 1970-04-30T17:00:00.000Z, 1970-05-31T17:00:00.000Z, 1970-06-30T17:00:00.000Z, 1970-07-31T17:00:00.000Z, 1970-08-31T17:00:00.000Z, 1970-09-30T17:00:00.000Z, 1970-10-31T18:00:00.000Z, 1970-11-30T18:00:00.000Z, 1970-12-31T18:00:00.000Z]

v3.1.2

TEST1 - expecting 0 instants
instants.size() == 1
instants: [1970-01-02T06:00:00.000Z]
java.lang.AssertionError: expected [0] but found [1]
Expected :0
Actual   :1


TEST2 - expecting 12 instants
Uh Oh! Next == Previous
instants.size() == 298
instants: [1970-01-01T18:00:00.000Z, 1970-01-02T18:00:00.000Z, 1970-01-03T18:00:00.000Z, 1970-01-04T18:00:00.000Z, 1970-01-05T18:00:00.000Z, 1970-01-06T18:00:00.000Z, 1970-01-07T18:00:00.000Z, 1970-01-08T18:00:00.000Z, 1970-01-09T18:00:00.000Z, 1970-01-10T18:00:00.000Z, 1970-01-11T18:00:00.000Z, 1970-01-12T18:00:00.000Z, 1970-01-13T18:00:00.000Z, 1970-01-14T18:00:00.000Z, 1970-01-15T18:00:00.000Z, 1970-01-16T18:00:00.000Z, 1970-01-17T18:00:00.000Z, 1970-01-18T18:00:00.000Z, 1970-01-19T18:00:00.000Z, 1970-01-20T18:00:00.000Z, 1970-01-21T18:00:00.000Z, 1970-01-22T18:00:00.000Z, 1970-01-23T18:00:00.000Z, 1970-01-24T18:00:00.000Z, 1970-01-25T18:00:00.000Z, 1970-01-26T18:00:00.000Z, 1970-01-27T18:00:00.000Z, 1970-01-28T18:00:00.000Z, 1970-01-29T18:00:00.000Z, 1970-01-30T18:00:00.000Z, 1970-01-31T18:00:00.000Z, 1970-02-01T18:00:00.000Z, 1970-02-02T18:00:00.000Z, 1970-02-03T18:00:00.000Z, 1970-02-04T18:00:00.000Z, 1970-02-05T18:00:00.000Z, 1970-02-06T18:00:00.000Z, 1970-02-07T18:00:00.000Z, 1970-02-08T18:00:00.000Z, 1970-02-09T18:00:00.000Z, 1970-02-10T18:00:00.000Z, 1970-02-11T18:00:00.000Z, 1970-02-12T18:00:00.000Z, 1970-02-13T18:00:00.000Z, 1970-02-14T18:00:00.000Z, 1970-02-15T18:00:00.000Z, 1970-02-16T18:00:00.000Z, 1970-02-17T18:00:00.000Z, 1970-02-18T18:00:00.000Z, 1970-02-19T18:00:00.000Z, 1970-02-20T18:00:00.000Z, 1970-02-21T18:00:00.000Z, 1970-02-22T18:00:00.000Z, 1970-02-23T18:00:00.000Z, 1970-02-24T18:00:00.000Z, 1970-02-25T18:00:00.000Z, 1970-02-26T18:00:00.000Z, 1970-02-27T18:00:00.000Z, 1970-02-28T18:00:00.000Z, 1970-03-01T18:00:00.000Z, 1970-03-02T18:00:00.000Z, 1970-03-03T18:00:00.000Z, 1970-03-04T18:00:00.000Z, 1970-03-05T18:00:00.000Z, 1970-03-06T18:00:00.000Z, 1970-03-07T18:00:00.000Z, 1970-03-08T18:00:00.000Z, 1970-03-09T18:00:00.000Z, 1970-03-10T18:00:00.000Z, 1970-03-11T18:00:00.000Z, 1970-03-12T18:00:00.000Z, 1970-03-13T18:00:00.000Z, 1970-03-14T18:00:00.000Z, 1970-03-15T18:00:00.000Z, 1970-03-16T18:00:00.000Z, 1970-03-17T18:00:00.000Z, 1970-03-18T18:00:00.000Z, 1970-03-19T18:00:00.000Z, 1970-03-20T18:00:00.000Z, 1970-03-21T18:00:00.000Z, 1970-03-22T18:00:00.000Z, 1970-03-23T18:00:00.000Z, 1970-03-24T18:00:00.000Z, 1970-03-25T18:00:00.000Z, 1970-03-26T18:00:00.000Z, 1970-03-27T18:00:00.000Z, 1970-03-28T18:00:00.000Z, 1970-03-29T18:00:00.000Z, 1970-03-30T18:00:00.000Z, 1970-03-31T18:00:00.000Z, 1970-04-01T18:00:00.000Z, 1970-04-02T18:00:00.000Z, 1970-04-03T18:00:00.000Z, 1970-04-04T18:00:00.000Z, 1970-04-05T18:00:00.000Z, 1970-04-06T18:00:00.000Z, 1970-04-07T18:00:00.000Z, 1970-04-08T18:00:00.000Z, 1970-04-09T18:00:00.000Z, 1970-04-10T18:00:00.000Z, 1970-04-11T18:00:00.000Z, 1970-04-12T18:00:00.000Z, 1970-04-13T18:00:00.000Z, 1970-04-14T18:00:00.000Z, 1970-04-15T18:00:00.000Z, 1970-04-16T18:00:00.000Z, 1970-04-17T18:00:00.000Z, 1970-04-18T18:00:00.000Z, 1970-04-19T18:00:00.000Z, 1970-04-20T18:00:00.000Z, 1970-04-21T18:00:00.000Z, 1970-04-22T18:00:00.000Z, 1970-04-23T18:00:00.000Z, 1970-04-24T18:00:00.000Z, 1970-04-25T18:00:00.000Z, 1970-04-26T18:00:00.000Z, 1970-04-27T17:00:00.000Z, 1970-04-28T17:00:00.000Z, 1970-04-29T17:00:00.000Z, 1970-04-30T17:00:00.000Z, 1970-05-01T17:00:00.000Z, 1970-05-02T17:00:00.000Z, 1970-05-03T17:00:00.000Z, 1970-05-04T17:00:00.000Z, 1970-05-05T17:00:00.000Z, 1970-05-06T17:00:00.000Z, 1970-05-07T17:00:00.000Z, 1970-05-08T17:00:00.000Z, 1970-05-09T17:00:00.000Z, 1970-05-10T17:00:00.000Z, 1970-05-11T17:00:00.000Z, 1970-05-12T17:00:00.000Z, 1970-05-13T17:00:00.000Z, 1970-05-14T17:00:00.000Z, 1970-05-15T17:00:00.000Z, 1970-05-16T17:00:00.000Z, 1970-05-17T17:00:00.000Z, 1970-05-18T17:00:00.000Z, 1970-05-19T17:00:00.000Z, 1970-05-20T17:00:00.000Z, 1970-05-21T17:00:00.000Z, 1970-05-22T17:00:00.000Z, 1970-05-23T17:00:00.000Z, 1970-05-24T17:00:00.000Z, 1970-05-25T17:00:00.000Z, 1970-05-26T17:00:00.000Z, 1970-05-27T17:00:00.000Z, 1970-05-28T17:00:00.000Z, 1970-05-29T17:00:00.000Z, 1970-05-30T17:00:00.000Z, 1970-05-31T17:00:00.000Z, 1970-06-01T17:00:00.000Z, 1970-06-02T17:00:00.000Z, 1970-06-03T17:00:00.000Z, 1970-06-04T17:00:00.000Z, 1970-06-05T17:00:00.000Z, 1970-06-06T17:00:00.000Z, 1970-06-07T17:00:00.000Z, 1970-06-08T17:00:00.000Z, 1970-06-09T17:00:00.000Z, 1970-06-10T17:00:00.000Z, 1970-06-11T17:00:00.000Z, 1970-06-12T17:00:00.000Z, 1970-06-13T17:00:00.000Z, 1970-06-14T17:00:00.000Z, 1970-06-15T17:00:00.000Z, 1970-06-16T17:00:00.000Z, 1970-06-17T17:00:00.000Z, 1970-06-18T17:00:00.000Z, 1970-06-19T17:00:00.000Z, 1970-06-20T17:00:00.000Z, 1970-06-21T17:00:00.000Z, 1970-06-22T17:00:00.000Z, 1970-06-23T17:00:00.000Z, 1970-06-24T17:00:00.000Z, 1970-06-25T17:00:00.000Z, 1970-06-26T17:00:00.000Z, 1970-06-27T17:00:00.000Z, 1970-06-28T17:00:00.000Z, 1970-06-29T17:00:00.000Z, 1970-06-30T17:00:00.000Z, 1970-07-01T17:00:00.000Z, 1970-07-02T17:00:00.000Z, 1970-07-03T17:00:00.000Z, 1970-07-04T17:00:00.000Z, 1970-07-05T17:00:00.000Z, 1970-07-06T17:00:00.000Z, 1970-07-07T17:00:00.000Z, 1970-07-08T17:00:00.000Z, 1970-07-09T17:00:00.000Z, 1970-07-10T17:00:00.000Z, 1970-07-11T17:00:00.000Z, 1970-07-12T17:00:00.000Z, 1970-07-13T17:00:00.000Z, 1970-07-14T17:00:00.000Z, 1970-07-15T17:00:00.000Z, 1970-07-16T17:00:00.000Z, 1970-07-17T17:00:00.000Z, 1970-07-18T17:00:00.000Z, 1970-07-19T17:00:00.000Z, 1970-07-20T17:00:00.000Z, 1970-07-21T17:00:00.000Z, 1970-07-22T17:00:00.000Z, 1970-07-23T17:00:00.000Z, 1970-07-24T17:00:00.000Z, 1970-07-25T17:00:00.000Z, 1970-07-26T17:00:00.000Z, 1970-07-27T17:00:00.000Z, 1970-07-28T17:00:00.000Z, 1970-07-29T17:00:00.000Z, 1970-07-30T17:00:00.000Z, 1970-07-31T17:00:00.000Z, 1970-08-01T17:00:00.000Z, 1970-08-02T17:00:00.000Z, 1970-08-03T17:00:00.000Z, 1970-08-04T17:00:00.000Z, 1970-08-05T17:00:00.000Z, 1970-08-06T17:00:00.000Z, 1970-08-07T17:00:00.000Z, 1970-08-08T17:00:00.000Z, 1970-08-09T17:00:00.000Z, 1970-08-10T17:00:00.000Z, 1970-08-11T17:00:00.000Z, 1970-08-12T17:00:00.000Z, 1970-08-13T17:00:00.000Z, 1970-08-14T17:00:00.000Z, 1970-08-15T17:00:00.000Z, 1970-08-16T17:00:00.000Z, 1970-08-17T17:00:00.000Z, 1970-08-18T17:00:00.000Z, 1970-08-19T17:00:00.000Z, 1970-08-20T17:00:00.000Z, 1970-08-21T17:00:00.000Z, 1970-08-22T17:00:00.000Z, 1970-08-23T17:00:00.000Z, 1970-08-24T17:00:00.000Z, 1970-08-25T17:00:00.000Z, 1970-08-26T17:00:00.000Z, 1970-08-27T17:00:00.000Z, 1970-08-28T17:00:00.000Z, 1970-08-29T17:00:00.000Z, 1970-08-30T17:00:00.000Z, 1970-08-31T17:00:00.000Z, 1970-09-01T17:00:00.000Z, 1970-09-02T17:00:00.000Z, 1970-09-03T17:00:00.000Z, 1970-09-04T17:00:00.000Z, 1970-09-05T17:00:00.000Z, 1970-09-06T17:00:00.000Z, 1970-09-07T17:00:00.000Z, 1970-09-08T17:00:00.000Z, 1970-09-09T17:00:00.000Z, 1970-09-10T17:00:00.000Z, 1970-09-11T17:00:00.000Z, 1970-09-12T17:00:00.000Z, 1970-09-13T17:00:00.000Z, 1970-09-14T17:00:00.000Z, 1970-09-15T17:00:00.000Z, 1970-09-16T17:00:00.000Z, 1970-09-17T17:00:00.000Z, 1970-09-18T17:00:00.000Z, 1970-09-19T17:00:00.000Z, 1970-09-20T17:00:00.000Z, 1970-09-21T17:00:00.000Z, 1970-09-22T17:00:00.000Z, 1970-09-23T17:00:00.000Z, 1970-09-24T17:00:00.000Z, 1970-09-25T17:00:00.000Z, 1970-09-26T17:00:00.000Z, 1970-09-27T17:00:00.000Z, 1970-09-28T17:00:00.000Z, 1970-09-29T17:00:00.000Z, 1970-09-30T17:00:00.000Z, 1970-10-01T17:00:00.000Z, 1970-10-02T17:00:00.000Z, 1970-10-03T17:00:00.000Z, 1970-10-04T17:00:00.000Z, 1970-10-05T17:00:00.000Z, 1970-10-06T17:00:00.000Z, 1970-10-07T17:00:00.000Z, 1970-10-08T17:00:00.000Z, 1970-10-09T17:00:00.000Z, 1970-10-10T17:00:00.000Z, 1970-10-11T17:00:00.000Z, 1970-10-12T17:00:00.000Z, 1970-10-13T17:00:00.000Z, 1970-10-14T17:00:00.000Z, 1970-10-15T17:00:00.000Z, 1970-10-16T17:00:00.000Z, 1970-10-17T17:00:00.000Z, 1970-10-18T17:00:00.000Z, 1970-10-19T17:00:00.000Z, 1970-10-20T17:00:00.000Z, 1970-10-21T17:00:00.000Z, 1970-10-22T17:00:00.000Z, 1970-10-23T17:00:00.000Z, 1970-10-24T17:00:00.000Z, 1970-10-25T17:00:00.000Z]
java.lang.AssertionError: expected [12] but found [298]
Expected :12
Actual   :298

NextExecution calculates wrong time

Sample cron expression

0 0/10 22 * * *

I run the core at 15:27, and it gives me the next execution time of 22:30, which should be 22:00. I believe it first look to the minute than hours, so next close value in range to 27 is 30.

ฤฐt should look first to year, month, etc, starting from bigger to smaller time unit.

Unix crontab nextExecution bug

I'm trying a simple unix crontab "* * * * 1", which should run every minute of every hour Mondays in every month, but nextExecution doesn't seem give the correct result. Following is the code:

    crontab = "* * * * 1";
    CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.UNIX);
    CronParser parser = new CronParser(cronDefinition);
    Cron cron = parser.parse(crontab);
    DateTime date = DateTime.now();
    System.out.println("now:" + date);
    ExecutionTime executionTime = ExecutionTime.forCron(cron);
    DateTime nextExecution = executionTime.nextExecution(date);
    DateTime lastExecution = executionTime.lastExecution(date);
    System.out.println("next execution:" + nextExecution);
    System.out.println("last execution:" + lastExecution);

Output:
now:2015-10-13T17:26:54.468-07:00
next execution:2015-11-02T00:00:00.000-08:00
last execution:2015-10-05T23:59:00.000-07:00

The last execution time seems correct, but next execution time should be 2015-10-19T00:00:00.000-07:00. It skips two weeks and also change the time zone. Can you guys take a look at it?

Thanks,
Jun

Next execution for day of week seems to be wrong

I just started using the library, but the first test I ran seems to fail. I'm trying to specify minute, hour of day, and day of week. It looks like late in month has trouble. Using version 3.1.2. If I get some time I'll try to dig, but it'll be a bit before that happens.

See the following.

public class ScheduleHelperTest
{
DateFormat dfSimple = new SimpleDateFormat("hh:mm:ss MM/dd/yyyy a");
DateFormat df = new SimpleDateFormat("hh:mm:ss EEE, MMM dd yyyy a");

@Test
public void testBasicCron() throws ParseException
{
    printDate("3:15:00 11/20/2015 PM");
    printDate("3:15:00 11/27/2015 PM");

// printDate("3:15:00 11/29/2015 PM");
// printDate("3:15:00 11/30/2015 PM");
// printDate("3:15:00 12/01/2015 PM");
// printDate("3:15:00 12/02/2015 PM");
// printDate("3:15:00 12/29/2015 PM");
// printDate("3:15:00 12/30/2015 PM");
// printDate("3:15:00 12/31/2015 PM");
}

private void printDate(String startDate) throws ParseException
{
    Date now = dfSimple.parse(startDate);
    System.out.println("Starting: "+ df.format(now));
    printNextDate(now, "0 6 * * 0");//Sunday
    printNextDate(now, "0 6 * * 1");
    printNextDate(now, "0 6 * * 2");
    printNextDate(now, "0 6 * * 3");
    printNextDate(now, "0 6 * * 4");
    printNextDate(now, "0 6 * * 5");
    printNextDate(now, "0 6 * * 6");
}

private void printNextDate(Date now, String cronString)
{
    Date date = nextSchedule(cronString, now);
    System.out.println("Next time: " + df.format(date));
}

public static Date nextSchedule(String cronString, Date lastExecution)
{
    DateTime now = new DateTime(lastExecution);
    CronParser cronParser =new CronParser(
            CronDefinitionBuilder.instanceDefinitionFor(CronType.UNIX));
    Cron cron = cronParser.parse(cronString);

    ExecutionTime executionTime = ExecutionTime.forCron(cron);
    DateTime nextExecution = executionTime.nextExecution(now);

    return nextExecution.toDate();
}

}

Starting: 03:15:00 Fri, Nov 20 2015 PM
Next time: 06:00:00 Sun, Nov 22 2015 AM
Next time: 06:00:00 Mon, Nov 23 2015 AM <-- As expected, one day later
Next time: 06:00:00 Tue, Nov 24 2015 AM
Next time: 06:00:00 Wed, Nov 25 2015 AM
Next time: 06:00:00 Thu, Nov 26 2015 AM
Next time: 06:00:00 Fri, Nov 27 2015 AM
Next time: 06:00:00 Sat, Nov 21 2015 AM
Starting: 03:15:00 Fri, Nov 27 2015 PM
Next time: 06:00:00 Sun, Nov 29 2015 AM
Next time: 06:00:00 Mon, Dec 07 2015 AM <-- Jumps an extra week
Next time: 06:00:00 Tue, Dec 08 2015 AM
Next time: 06:00:00 Wed, Dec 02 2015 AM
Next time: 06:00:00 Thu, Dec 03 2015 AM
Next time: 06:00:00 Fri, Dec 04 2015 AM
Next time: 06:00:00 Sat, Nov 28 2015 AM

Better validation message

First of all thanks for sharing this project!

"Expressions size do not match registered options!" is not really helpful, wrong spelling and somewhat a technical detail.
This should be something like: "Cron expression contains x parts but we expect one of [6,7]"

Valid quartz range expression is considered invalid. Range validations should be contextual to cron definition.

Version 3.1.0
Quartz parser fails for day-of-week range of SUN-SAT. The Quartz definition appears correct but when the Quartz-based parser is created, the "DAY_OF_WEEK" constraints.stringMapping values are incorrect. MON = 1 even though mondayDoWValue = 2.

Use following test in CronParserQuartzIntegrationTest to reproduce:
@test
public void testSunToSat() {
// FAILS SUN-SAT: SUN = 7 and SAT = 6
parser.parse("0 0 12 ? * SUN-SAT");
}

Stack trace:
java.lang.IllegalArgumentException: Bad range defined! Defined range should satisfy from <= to, but was [%s, %s]
at com.cronutils.model.field.expression.Between.validate(Between.java:63)
at com.cronutils.model.field.expression.Between.(Between.java:39)
at com.cronutils.model.field.expression.Between.(Between.java:31)
at com.cronutils.parser.field.FieldParser.parseBetween(FieldParser.java:93)
at com.cronutils.parser.field.FieldParser.parse(FieldParser.java:69)
at com.cronutils.parser.field.CronParserField.parse(CronParserField.java:68)
at com.cronutils.parser.CronParser.parse(CronParser.java:96)
at com.cronutils.parser.CronParserQuartzIntegrationTest.testSunToSat(CronParserQuartzIntegrationTest.java:129)

refer to debug values below:
image

ExecutionTime.lastExecution doesn't work

The following test case fails:

DateTime now = DateTime.now();
CronParser parser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ));
ExecutionTime executionTime = ExecutionTime.forCron(parser.parse("0 0 3 ? * 6"));
DateTime last = executionTime.lastExecution(now);
DateTime next = executionTime.nextExecution(now);
Assert.assertNotEquals(last, next);

I expected the last to be "3 AM of the nearest Saturday looking past from now" and next to be "3 AM of the nearest Saturday looking forward". Am I wrong with my expectations?

String constants (months and day of week) not working as expected

Hi,

the string constants are not working as expected. One can code "MON-FRI" for the day of the week, but "MON" for "just on mondays" doesn't work.

Same issue for the month. "JAN-FEB" is ok, but "JAN" for just january doesn't work, too.

Furthermore, some months doenst work with "from-to". With cron definition QUARTZ I've tried to pass "0 0 0 * JUL-AUG * *" - but this results in a

Exception in thread "main" java.lang.IllegalArgumentException: Values must not be empty
at org.apache.commons.lang3.Validate.notEmpty(Validate.java:300)
at com.cronutils.model.time.TimeNode.<init>(TimeNode.java:26)
at com.cronutils.model.time.ExecutionTimeBuilder.forMonthsMatching(ExecutionTimeBuilder.java:83)
at com.cronutils.model.time.ExecutionTime.forCron(ExecutionTime.java:83)
at mk.test.Main.testCronUtilsOwnDefinition(Main.java:42)
at mk.test.Main.main(Main.java:21)

My fault?

Thanks, Michael

Incorrect parsing of step values: "0 1/2 * * *" -> "0 /2 * * *"

When using UNIX cron type (other not tested), the cron parser produces incorrect values for some step values (the one with slashes), e.g.:

Input Parser output Comment
0 0/1 * * * 0 * * * The hour field is missing/empty.
0 1/2 * * * 0 /2 * * * The hour field should remain 1/2.
0/1 * * * * * * * * The minute field is missing/emtpy.
1/2 * * * * /2 * * * * The minute field should remain 1/2.

Test used with cron-utils:3.1.2:

public class StepValuesTest {

    private CronParser cronParser;

    @Before
    public void setup() {
        CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.UNIX);
        cronParser = new CronParser(cronDefinition);
    }

    @Test
    public void everyEvenHourShouldBeParsedCorrectly() {
        Cron cron = cronParser.parse("0 0/1 * * *");
        assertThat(cron.asString(), anyOf(is("0 0/1 * * *"), is("0 /1 * * *"), is("0 * * * *")));
    }

    @Test
    public void everyOddHourShouldBeParsedCorrectly() {
        Cron cron = cronParser.parse("0 1/2 * * *");
        assertThat(cron.asString(), is("0 1/2 * * *"));
    }

    @Test
    public void everyEvenMinuteShouldBeParsedCorrectly() {
        Cron cron = cronParser.parse("0/1 * * * *");
        assertThat(cron.asString(), anyOf(is("0/1 * * * *"), is("/1 * * * *"), is("* * * * *")));
    }

    @Test
    public void everyOddMinuteShouldBeParsedCorrectly() {
        Cron cron = cronParser.parse("1/2 * * * *");
        assertThat(cron.asString(), is("1/2 * * * *"));
    }

}

nextExecution not working for '0/30 * * * * *'

For the cron expression 0/30 * * * * *, with a custom definition of:

// Unix Crontab with seconds allowed
CronDefinition cronDefinition = CronDefinitionBuilder.defineCron()
.withSeconds().and()
.withMinutes().and()
.withHours().and()
.withDayOfMonth().and()
.withMonth().and()
.withDayOfWeek().withValidRange(0, 7).withMondayDoWValue(1).withIntMapping(7, 0).and()
.instance();

Performing a nextExecution call when the input DateTime value is between 30 and 59 seconds of the minute causes the method to return with a DateTime at the previous minute instead of the next minute.

For example:

nextExecution('2015-08-28 12:05:44') -> 2015-08-28 12:05:00
nextExecution('2015-08-28 12:05:14') -> 2015-08-28 12:05:30

Note that I've also tried this with '*/30 * * * * *' and got the same results.

Unit tests:

@Test
public void testCronExpressionAfterHalf() {

    CronDefinition cronDefinition = CronDefinitionBuilder.defineCron()
            .withSeconds().and()
            .withMinutes().and()
            .withHours().and()
            .withDayOfMonth().and()
            .withMonth().and()
            .withDayOfWeek().withValidRange(0, 7).withMondayDoWValue(1).withIntMapping(7, 0).and()
            .instance();

    CronParser parser = new CronParser(cronDefinition);
    Cron cron = parser.parse("*/30 * * * * *");

    MutableDateTime mutableDateTime = new MutableDateTime();
    mutableDateTime.setDateTime(2015, 8, 28, 12, 5, 44, 0);

    DateTime startDateTime = mutableDateTime.toDateTime();

    mutableDateTime = new MutableDateTime();
    mutableDateTime.setDateTime(2015, 8, 28, 12, 6, 0, 0);

    DateTime expectedDateTime = mutableDateTime.toDateTime();

    ExecutionTime executionTime = ExecutionTime.forCron(cron);

    DateTime nextExecutionDateTime = executionTime.nextExecution(startDateTime);
    MatcherAssert.assertThat(nextExecutionDateTime, Matchers.equalTo(expectedDateTime));
}

@Test
public void testCronExpressionBeforeHalf() {

    CronDefinition cronDefinition = CronDefinitionBuilder.defineCron()
            .withSeconds().and()
            .withMinutes().and()
            .withHours().and()
            .withDayOfMonth().and()
            .withMonth().and()
            .withDayOfWeek().withValidRange(0, 7).withMondayDoWValue(1).withIntMapping(7, 0).and()
            .instance();

    CronParser parser = new CronParser(cronDefinition);
    Cron cron = parser.parse("0/30 * * * * *");

    MutableDateTime mutableDateTime = new MutableDateTime();
    mutableDateTime.setDateTime(2015, 8, 28, 12, 5, 14, 0);

    DateTime startDateTime = mutableDateTime.toDateTime();

    mutableDateTime = new MutableDateTime();
    mutableDateTime.setDateTime(2015, 8, 28, 12, 5, 30, 0);

    DateTime expectedDateTime = mutableDateTime.toDateTime();

    ExecutionTime executionTime = ExecutionTime.forCron(cron);

    DateTime nextExecutionDateTime = executionTime.nextExecution(startDateTime);
    MatcherAssert.assertThat(nextExecutionDateTime, Matchers.equalTo(expectedDateTime));
}

Buggy or wrong Cron4j Definition? Impossible to set an "Every X times" crons.

Hello, if I'm not wrong the Cron4j definition is wrong for "Every X times" crons.

In Cron4J Manual (http://goo.gl/2CgX2h) to have a task execute every 5 minutes you use:
***/5 * * * ***
And to have the task execute at XX:05 you use:
**5 * * * ***

Now there seems to be some special case in between which Cron4J supports but its not documented anywhere nor I found it in the manuals and examples of Cron4J and its this one:
**/5 * * * ***

_BUG:_
Cron-Utils interprets "/5 * * * *" as "Every 5 minutes" but in Cron4J it is being interpreted as "once a hour at 05min".

Now this gets even worse with the Cron-Utils mapper. As it maps any "/x" and "/x" to "/x". So even if you map from CRON4J to CRON4J it breaks each cron string removing the "" having a unexpected result.

Here is a demo code:

    public static void main(String[] args) {

        // Cron Definitions & Parsers
        CronDefinition unixCronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.UNIX);
        CronDefinition cron4jCronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.CRON4J);
        CronParser unixParser = new CronParser(unixCronDefinition);
        CronParser cron4jParser = new CronParser(unixCronDefinition);

        // From UNIX to CRON4J Mapper
        CronMapper cronMapper = new CronMapper(unixCronDefinition, cron4jCronDefinition);

        // ISSUE 1: Every X times BUG for CRON4J
        System.out.println(cronMapper.map(unixParser.parse("*/5 * * * *")).asString()); // WRONG - /5 * * * * (Should be */5 * * * *)
        System.out.println(cronMapper.map(unixParser.parse("/5 * * * *")).asString()); // WRONG - /5 * * * * (Should be */5 * * * *)
        System.out.println(cronMapper.map(unixParser.parse("5 * * * *")).asString()); // OK - 5 * * * *

        // Descriptor 
        CronDescriptor descriptor = CronDescriptor.instance(Locale.UK);
        System.out.println(descriptor.describe(cron4jParser.parse("*/5 * * * *"))); // OK - every 5 minutes
        System.out.println(descriptor.describe(cron4jParser.parse("/5 * * * *")));  // WRONG - every 5 minutes (Should be "every hour at minute 5")
        System.out.println(descriptor.describe(cron4jParser.parse("5 * * * *")));   // OK - every hour at minute 5

        // ISSUE 2: (Not so important)
        // This is not supported by Cron-Utils
        // In Cron4J Manual http://www.sauronsoftware.it/projects/cron4j/manual.php
        System.out.println(descriptor.describe(cron4jParser.parse("0 5 * * *|8 10 * * *|22 17 * * *")));
        System.out.println(cronMapper.map(unixParser.parse("0 5 * * *|8 10 * * *|22 17 * * *")).asString()); 

    }

Basically the only thing missing here is that Cron-Utils shouls add a "/x" to the evey x times definition. And to parse "/x" the same as "x" and not as "/x".

Thanks for the awesome work done so far. Cron-Utils is just awesome useful.

Build continuous delivery pipeline for SNAPSHOT artifacts

Setup a continuous delivery pipeline for SNAPSHOT artifacts to ensure they are published to Maven central with latest fixes and features being pushed to the github repository. This way, anyone depending on the latest version, would benefit from them as soon as changes are pushed.

Buggy Descriptor: Throwing ArrayIndexOutOfBoundsException and UnknownFormatConversionExceptions

Hello, is it a known issue that the descriptor is throwing weird formatting and array exceptions?

This is my code:

try{
String cronExpr = "% HERE CRON EXPRESSION %";
  CronDefinition cronDefinition =  CronDefinitionBuilder.instanceDefinitionFor(CronType.CRON4J);
  CronParser parser = new CronParser(cronDefinition);
  CronDescriptor descriptor = CronDescriptor.instance(Locale.UK);
  descriptor.describe(parser.parse(cronExpr)
}catch(Exception e){
        e.printStackTrace();
}

When I use:
*** 1,2,3,4,5,6 * 1,2,3 ***
I get:
java.lang.ArrayIndexOutOfBoundsException: 1

When I use:
* 1 1,2 * 4
I get:
java.util.UnknownFormatConversionException: Conversion = 'p'

Both examples return TRUE while validation.

More info:
CronType.CRON4J and CronType.UNIX has the same bug.
CronUtils 3.0 and 2.0 has the same bug.

I have htime-1.0, joda-time-2.4, guava-18.0, commons-lang3-3.4 in the classpath.

Could somebody try to replicate the issue?

nextExecution not working as expected

Hi,

today I've played around with cron-utils but all my efforts to get it working as I expected failed....

I've tried to get a cron expression working that "executes" every monday at 18:00. My efforts to use a custom cron definition failed, so I tried the built-in CRON4J expression....

Here is what I've did:

  // every monday at 18:00
  final String cronExpression = "0 18 * * 1";

  final CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.CRON4J);
  final CronValidator validator = new CronValidator(cronDefinition);
  System.out.println("isValid:" + validator.isValid(cronExpression));

  final CronParser parser = new CronParser(cronDefinition);
  final Cron cron = parser.parse(cronExpression);

  final ExecutionTime executionTime = ExecutionTime.forCron(cron);

  final CronDescriptor descriptor = CronDescriptor.instance(Locale.UK);
  System.out.println(descriptor.describe(cron));

  final DateTime now = DateTime.now();
  final DateTime nextExec = executionTime.nextExecution(now);
  final DateTime overNextExec = executionTime.nextExecution(nextExec);

  System.out.println("now is: " + now);
  System.out.println("next exec time: " + nextExec);
  System.out.println("over next exec time: " + overNextExec);

This results in

isValid:true
at 18:00 at Monday day
now is: 2015-07-01T17:02:07.556+02:00
next exec time: 2015-07-01T18:00:00.000+02:00
over next exec time: 2015-07-01T18:00:00.000+02:00 

As you can see, the date is always the 1st of july, whis is in fact no monday. I also don't understand why the "overnext time" is the same as the "next time".

Anything I did wrong?

Thank you, Michael

cron-utils fails to describe weekday list

When we select cron expression as weekly Monday and Tuesday the expression is coming like
0 0 0 ? * MON,TUE *

exception got while parsing the cron expression.

in hours tab
image

image

UNIX Cron to QUARTZ expressions incorrect

Quartz does not support specifying both a day-of-week and a day-of-month value (you must currently use the '?' character in one of these fields).

For example: 0 0 0 * * 1 * is not legal in quartz, instead it must be: 0 0 0 ? * 1 *

Code such as:

        CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.UNIX);
        CronDefinition quartzDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ);
        CronMapper cronMapper = new CronMapper(cronDefinition, quartzDefinition);
        CronParser cronParser = new CronParser(cronDefinition);
        Cron cronCron = cronParser.parse(cronString);
        Cron quartzCron = cronMapper.map(cronCron);
        String result = quartzCron.asString();

Will take 0 0/2 * * * and produce 0 /2 * * * * - it should be 0 /2 * * ? * (or possibly 0 /2 ? * * *)

Using quartz 2.2.2 and cron-utils 3.1.2

Week days are not recognized

The following fails as invalid cron expression:

CronParser parser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ));
ExecutionTime executionTime = ExecutionTime.forCron(parser.parse("0 0 3 ? * SAT"));

Throws java.lang.IllegalArgumentException: Invalid value. Expected some integer, found SAT

The expression was actually taken from working quartz 2.2.1 based system.

Expose `ExecutionTime` as public

Why is ExecutionTime (the class) not exposed as a publicly accessible class?

It would be very handy to get access to it instead of copy+pasting the class into application code.

lastExecution returns incorrect value

Here's a new unit test for ExecutionTimeIntegrationTest. The println is just for debugging.

@Test
public void testHourlyIntervalTimeFromLastExecution() throws Exception {
    DateTime now = DateTime.now();
    int hour = now.getHourOfDay();
    int previousHour = (hour > 0) ? (hour - 1) : 23;
    String quartzCronExpression = "0 0 " + previousHour + " * * ?";
    ExecutionTime executionTime = ExecutionTime.forCron(quartzCronParser.parse(quartzCronExpression));
    System.out.println(executionTime.lastExecution(now));
    assertTrue(executionTime.timeFromLastExecution(now).getStandardMinutes() <= 120);
}

Unix crontab

Hi,
I have the following in my unix crontab :
00 21 * * * /appli/run.sh

When I executed
ExecutionTime executionTime = ExecutionTime.forCron(parser.parse(cron));

I have the following error
Class java.lang.IllegalArgumentException Message Invalid chars in expression! Expression: APPLI Invalid chars: APPLI

Does I miss somehting ?

Here is the complete source code :

   CronDefinition cronDefinition =
        CronDefinitionBuilder.defineCron()
        .withSeconds().and()
        .withMinutes().and()
        .withHours().and()
        .withDayOfMonth()
        .supportsHash().supportsL().supportsW().and()
        .withMonth().and()
        .withDayOfWeek()
        .withIntMapping(7, 0) //we support non-standard non-zero-based numbers!
        .supportsHash().supportsL().supportsW().and()
        .withYear().and()
        .lastFieldOptional()
        .instance();

    //or get a predefined instance
    cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(com.cronutils.model.CronType.QUARTZ);
    //create a parser based on provided definition
    CronParser parser = new CronParser(cronDefinition);
    DateTime now = DateTime.now();
    ExecutionTime executionTime = ExecutionTime.forCron(parser.parse(cron));
    DateTime nextExecution = executionTime.nextExecution(now);

timeToNextExecution returns Duration evaluates to 0

Firstly very nice tool! thank you!

Now the problem:

  1. I use the tool to schedule tasks to be executed periodically
  2. The following code calculates the next time the job shall be invoked:
@Override
void schedule(AppJobManager manager, _Job job) {
    DateTime now = DateTime.now();
    ExecutionTime executionTime = ExecutionTime.forCron(cron);
    Duration nextExecution = executionTime.timeToNextExecution(now);
    long seconds = nextExecution.getStandardSeconds();
    manager.executor().schedule(job, seconds, TimeUnit.SECONDS);
}

Problem is I found it always returns 0 for the next time. Thus the job is executed huge number of times (>300) while I expected it to be executed for 1 time. The schedule method is called after the time is due and job is executed.

Typo in README.md

Hello,

I just started using your library and noticed a typo in the README.md file.

On line:

//Get date for next execution
DateTime nextExecution = executionTime.timeToNextExecution(now));

Should be:

//Get date for next execution
DateTime nextExecution = executionTime.nextExecution(now));

Hope it helps.

Quartz: offset from the last day of the month currently not supported

Description: Currently expressions with an offset from the last day of the month are not supported. The feature is described at Quartz tutorial.
Example:
CronParser parser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ));
parser.parse("* * * L-3 * ?");
Output: java.lang.IllegalArgumentException: java.lang.NumberFormatException: For input string: "L"
Expected behaviour: Properly parse the expression, without throwing an exception.

lastExecution and nextExecution do not work

for everything other than a dayOfWeek value == 1, nextExecution and lastExecution do not return correct results. Example below should return a date of 9/23 while it returns 9/30. Similiarly other days return a week further then they should. Only 1 gives the correct value of 9/21. I tried with previous versions of this and although i get a different result, it is still wrong. with previous versions i always get the next day (9/19)

CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.UNIX);
CronParser parser = new CronParser(cronDefinition);
Cron myCron = parser.parse("0 3 * * 3");
DateTime now = DateTime.now();
ExecutionTime executionTime = ExecutionTime.forCron(myCron);
DateTime nextExecution = executionTime.nextExecution(now);

nextExecution() does not respect months (and possibly weeks)

If you write a cron expression that contains a month or day of week, nextExection() ignores it.

Example where it's supposed to fire at 11:11 on November 11th:

    CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ);
    CronParser cronParser = new CronParser(cronDefinition);
    ExecutionTime executionTime = ExecutionTime.forCron(cronParser.parse("0 11 11 11 11 ?"));
    CronDescriptor descriptor = CronDescriptor.instance(Locale.UK);
    System.out.println(descriptor.describe(cronParser.parse("0 11 11 11 11 ?")));
    DateTime now = DateTime.now();
    DateTime whenToExecuteNext = executionTime.nextExecution(now);
    System.out.println(whenToExecuteNext.toString("yyyy/MM/dd HH:mm:ss", Locale.ENGLISH));

output is

at 11:11 at 11 day at November month
2015/03/02 11:11:00

So it (sort of) got the descriptor right, but the nextExecution() is actually in the past.

BetweenDayOfWeekValueGenerator does not work for the first day of a month in some cases.

With this CronDefinition:

CronDefinitionBuilder.defineCron()
            .withMinutes().and()
            .withHours().and()
            .withDayOfMonth()
                .supportsL().supportsW()
                .and()
            .withMonth().and()
            .withDayOfWeek()
                .withMondayDoWValue(1)
                .withValidRange(1, 7)
                .supportsHash().supportsL()
                .and()
            .withYear().and()
                .lastFieldOptional()

and this cron expression:
"30 3 * * MON-FRI"

executionTime.nextExecution(1970-01-01....) returns 1970-01-02... instead of 1970-01-01. It seems to always do this on when the first day of the month falls within the DoW range. It looks like an initial case type bug. The BetweenDayOfWeekValueGenerator will never return the value 1 in this case - only the value 8.

The result is that if I execute this over the year of 1970, I'd expect to get 261 days. Instead I get 245 days.

Based on this bug for the year 1970, I'd expect to lose 8 days. I'm losing 16 days though. Taking a quick look at the output, it appears the same issue exists for the last day of the month when the last day falls within the DoW range. For example, cron-utils will not return 1970-07-31 (a Friday) for the above cron expression.

every 2 min schedule doesn't roll over to next hour

Sorry for scala, but it should be pretty clear:

object CronError extends App {
  val cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.UNIX)
  val cron = new CronParser(CronSchedule.cronDefinition).parse("0 */2 * * * *")
  val executionTime = ExecutionTime.forCron(cron)
  val time = DateTime.parse("2015-09-05T13:56:00.000-07:00")
  val next = executionTime.nextExecution(time)
  val shouldBeInNextHour = executionTime.nextExecution(next)
  val text = CronDescriptor.instance(Locale.US).describe(cron)
  println(s"Schedule: $text")
  println(s"Original time: $time")
  println(s"Next event time: $next")
  println(s"It should be in next hour: $shouldBeInNextHour")
}

Library version: 3.1.0

PS. at the same time pattern "0 */1 * * * *" works fine.

Support Java 8 time

Current implementation was developed using joda-time, since provides great date and time utils.
A new date, time and calendar API was released at Java 8. Since we want to keep compatibility with previous JDK versions, we want to maintain a branch with joda-time dependency, and another to leverage the new Java 8 API.

Expressions containing multiple days of the week are not handled correctly.

For cron expressions where multiple days of the week are defined, the lastExecution can be wrong. For example the expression "* * * * 1,2" which should run every minute on Mondays and Tuesdays will actually be treated as running every minute on the 1st and 2nd of the month.

This appears to be due to FieldValueGeneratorFactory not handling "And" expressions correctly for Day of Week. On/Between expressions are handled by specific OnDayOfWeekValueGenerator and BetweenDayOfWeekValueGenerate, but And expressions are handled by the AndFieldValueGenerator.

lastExecution can return dates in the future when cron definition contains "day of week"

For cron expressions which specify a day of the week to run on, ExecutionTime.lastExecution can return a date in the future if the last execution was in the previous month.

E.g. for cron expression "0 11 * * 1" if you try to get the last execution as of "2015-11-02T00:10:00" it will return "2015-11-16T11:00:00" rather than "2015-10-26T11:00:00" as expected.

This appear to be due to a bug in TimeNode.java which doesn't return the correct shift value when getting the nearest backward value.

It looks like a similar bug was fixed in TimeNode.getNearestForwardValue as part of issue #37

Extract DateTimeFormatBuilder to another project

Extract DateTimeFormatBuilder and related classes to another project, since is not cron functionality.
Import that project as Maven dependency, so that we can still use those classes for time formatting.

JodaTime throws exception on hour shifting

This code:

CronDefinition definition = CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ);
CronParser parser = new CronParser(definition);
Cron cron = parser.parse("0/1 * * 1/1 * ? *");  // Every 1 second
ExecutionTime executionTime = ExecutionTime.forCron(cron);

DateTime now = new DateTime().withTime(23, 59, 59, 0);
DateTime nextExecution = executionTime.nextExecution(now);

Would throw this exception: org.joda.time.IllegalFieldValueException: Value 24 for hourOfDay must be in the range [0,23] because 23:59:59 + 1 second would become 24:00:00, which is an invalid hour for a new DateTime object.

java.lang.NoClassDefFoundError com/google/common/collect/Maps

Hello there, I am using the jar file from maven central cron-utils-1.1.2.jar

this is my code following the examples :

package com.mh.cronParser;

import com.cronutils.descriptor.CronDescriptor;
import com.cronutils.model.Cron;
import com.cronutils.model.CronType;
import com.cronutils.model.definition.CronDefinition;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.parser.CronParser;
import com.cronutils.validator.CronValidator;

import java.util.Locale;

public class Main {

    public static void main(String[] args) {
        CronDefinition cronDefinition =
                CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ);

        //create a parser based on provided definition
        CronParser parser = new CronParser(cronDefinition);
        Cron quartzCron = parser.parse("0 23 ? * * 1-5 *");
        System.out.println(quartzCron.asString());

        //create a descriptor for a specific Locale
        CronDescriptor descriptor = CronDescriptor.instance(Locale.UK);

        //parse some expression and ask descriptor for description
        String description = descriptor.describe(parser.parse("*/45 * * * * *"));
        //description will be: "every 45 seconds"

        //Validate if a string expression matches a cron definition:
        CronValidator quartzValidator = new CronValidator(cronDefinition);

        //getting a boolean result:
        quartzValidator.isValid("0 23 ? * * MON-FRI *");

        //or returning same string if valid and raising an exception if invalid
        quartzValidator.validate("0 23 ? * * MON-FRI *");
    }
}

and I got this exception when I tried to run the program

Exception in thread "main" java.lang.NoClassDefFoundError: com/google/common/collect/Maps
    at com.cronutils.model.definition.CronDefinitionBuilder.<init>(CronDefinitionBuilder.java:39)
    at com.cronutils.model.definition.CronDefinitionBuilder.defineCron(CronDefinitionBuilder.java:48)
    at com.cronutils.model.definition.CronDefinitionBuilder.quartz(CronDefinitionBuilder.java:152)
    at com.cronutils.model.definition.CronDefinitionBuilder.instanceDefinitionFor(CronDefinitionBuilder.java:188)
    at com.mh.cronParser.Main.main(Main.java:16)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: java.lang.ClassNotFoundException: com.google.common.collect.Maps
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
    ... 10 more

looks like the built jar is not fine

Parser exception handling

Parsing can throw different kinds of exceptions. It would be nice if it would always throw IllegalArgumentException

eg:
parsing * *[triple space here]* * ? throws a NumberFormatException with message For input string: ""

java.lang.NumberFormatException: For input string: ""
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:592)
    at java.lang.Integer.parseInt(Integer.java:615)
    at com.cronutils.model.field.constraint.FieldConstraints.stringToInt(FieldConstraints.java:70)
    at com.cronutils.model.field.On.<init>(On.java:28)
    at com.cronutils.parser.field.FieldParser.parse(FieldParser.java:46)
    at com.cronutils.parser.field.CronParserField.parse(CronParserField.java:68)
    at com.cronutils.parser.CronParser.parse(CronParser.java:86)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.