验证 cron 表达式在 Java 中是否有效

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2362985/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 06:35:09  来源:igfitidea点击:

Verifying a cron expression is valid in Java

javacronquartz-scheduler

提问by Omar Kooheji

I'm writing a scheduling application in Java using Quartz. I'm using the CronTrigger, but my cron expressions are entered into a database before they are scheduled and are based on user input.

我正在使用 Quartz 在 Java 中编写调度应用程序。我正在使用 CronTrigger,但我的 cron 表达式在它们被调度之前被输入到数据库中,并且基于用户输入。

Is there a way I can verify that the cron expressions are valid when I capture them? I'd rather do this and give the user an appropriate error message than wait until the scheduler is run and I get a ParseException when I try and create the trigger. Which could be days after the user inputs the data.

有没有办法在捕获 cron 表达式时验证它们是否有效?我宁愿这样做并为用户提供适当的错误消息,而不是等到调度程序运行并在我尝试创建触发器时收到 ParseException。这可能是用户输入数据后的几天。

采纳答案by sfussenegger

Can't you simply create a trigger without actually executing it? You could simply give appropriate feedback in case of a ParseException. If the expression is okay, persist the expression to DB.

你不能简单地创建一个触发器而不实际执行它吗?在 ParseException 的情况下,您可以简单地提供适当的反馈。如果表达式没问题,则将表达式持久化到 DB。

Edit: or simply do this:

编辑:或者干脆这样做:

org.quartz.CronExpression.isValidExpression(expression);

回答by Leo

I've modified the following codeadded by @ph4r05 to generate a regex as well; here's the regex:

我修改了@ph4r05 添加的以下代码以生成正则表达式;这是正则表达式:

^\s*($|#|\w+\s*=|(\?|\*|(?:[0-5]?\d)(?:(?:-|\/|\,)(?:[0-5]?\d))?(?:,(?:[0-5]?\d)(?:(?:-|\/|\,)(?:[0-5]?\d))?)*)\s+(\?|\*|(?:[0-5]?\d)(?:(?:-|\/|\,)(?:[0-5]?\d))?(?:,(?:[0-5]?\d)(?:(?:-|\/|\,)(?:[0-5]?\d))?)*)\s+(\?|\*|(?:[01]?\d|2[0-3])(?:(?:-|\/|\,)(?:[01]?\d|2[0-3]))?(?:,(?:[01]?\d|2[0-3])(?:(?:-|\/|\,)(?:[01]?\d|2[0-3]))?)*)\s+(\?|\*|(?:0?[1-9]|[12]\d|3[01])(?:(?:-|\/|\,)(?:0?[1-9]|[12]\d|3[01]))?(?:,(?:0?[1-9]|[12]\d|3[01])(?:(?:-|\/|\,)(?:0?[1-9]|[12]\d|3[01]))?)*)\s+(\?|\*|(?:[1-9]|1[012])(?:(?:-|\/|\,)(?:[1-9]|1[012]))?(?:L|W)?(?:,(?:[1-9]|1[012])(?:(?:-|\/|\,)(?:[1-9]|1[012]))?(?:L|W)?)*|\?|\*|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(?:(?:-)(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?(?:,(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(?:(?:-)(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)*)\s+(\?|\*|(?:[0-6])(?:(?:-|\/|\,|#)(?:[0-6]))?(?:L)?(?:,(?:[0-6])(?:(?:-|\/|\,|#)(?:[0-6]))?(?:L)?)*|\?|\*|(?:MON|TUE|WED|THU|FRI|SAT|SUN)(?:(?:-)(?:MON|TUE|WED|THU|FRI|SAT|SUN))?(?:,(?:MON|TUE|WED|THU|FRI|SAT|SUN)(?:(?:-)(?:MON|TUE|WED|THU|FRI|SAT|SUN))?)*)(|\s)+(\?|\*|(?:|\d{4})(?:(?:-|\/|\,)(?:|\d{4}))?(?:,(?:|\d{4})(?:(?:-|\/|\,)(?:|\d{4}))?)*))$

Here's the java code:

这是Java代码:

private static String cronRegex = null;

public static String getCronRegex()
{
  if (cronRegex == null)
  {
    // numbers intervals and regex
    Map<String, String> numbers = new HashMap<String, String>();
    numbers.put("sec", "[0-5]?\d");
    numbers.put("min", "[0-5]?\d");
    numbers.put("hour", "[01]?\d|2[0-3]");
    numbers.put("day", "0?[1-9]|[12]\d|3[01]");
    numbers.put("month", "[1-9]|1[012]");
    numbers.put("dow", "[0-6]");
    numbers.put("year", "|\d{4}");

    Map<String, String> field_re = new HashMap<String, String>();

    // expand regex to contain different time specifiers
    for (String field : numbers.keySet())
    {
      String number = numbers.get(field);
      String range = "(?:" + number + ")(?:(?:-|\/|\," +  ("dow".equals(field)? "|#" :    "") + 

        ")(?:" + number + "))?" +  ("dow".equals(field)? "(?:L)?" : ("month".equals(field)? "(?:L|W)?" : ""));
      field_re.put(field, "\?|\*|" + range + "(?:," + range + ")*");
    }

    // add string specifiers
    String monthRE = field_re.get("month");
    String monthREVal =   "JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC";
    String monthRERange = "(?:" + monthREVal + ")(?:(?:-)(?:" + monthREVal + "))?" ; 
    monthRE = monthRE +  "|\?|\*|" + monthRERange + "(?:," + monthRERange + ")*" ;
    field_re.put("month", monthRE);

    String dowRE = field_re.get("dow");
    String dowREVal = "MON|TUE|WED|THU|FRI|SAT|SUN";
    String dowRERange = "(?:" + dowREVal + ")(?:(?:-)(?:" + dowREVal + "))?" ;

    dowRE = dowRE + "|\?|\*|" + dowRERange + "(?:," + dowRERange + ")*" ;
    field_re.put("dow", dowRE);

    StringBuilder fieldsReSB = new StringBuilder();
    fieldsReSB.append("^\s*(").append("$") //
      .append("|#") //
      .append("|\w+\s*=") //
      .append("|") //
      .append("(")//
      .append(field_re.get("sec")).append(")\s+(")//
      .append(field_re.get("min")).append(")\s+(")//
      .append(field_re.get("hour")).append(")\s+(")//
      .append(field_re.get("day")).append(")\s+(")//
      .append(field_re.get("month")).append(")\s+(")//
      .append(field_re.get("dow")).append(")(|\s)+(")//
      .append(field_re.get("year"))//
      .append(")")//
      .append(")")//
      .append("$");

    cronRegex = fieldsReSB.toString();
  }
  return cronRegex;
}

I'm by no means a regex expert, but at least this seems to work on all the examples given by the quartz documentation

我绝不是正则表达式专家,但至少这似乎适用于石英文档给出的所有示例

回答by jovanchohan

I found the following regular expression in the "QuartzNet" project on Github. I think it may be what Quartz uses to validate cron expressions.

我在 Github 上的“QuartzNet”项目中找到了以下正则表达式。我认为这可能是 Quartz 用来验证 cron 表达式的方法。

Link: https://github.com/quartznet/quartznet/blob/master/src/Quartz/Xml/job_scheduling_data_2_0.xsd

链接:https: //github.com/quartznet/quartznet/blob/master/src/Quartz/Xml/job_scheduling_data_2_0.xsd

(((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)|(([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))|([\?])|([\*]))[\s](((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)|(([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))|([\?])|([\*]))[\s](((([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?,)*([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?)|(([\*]|[0-9]|[0-1][0-9]|[2][0-3])/([0-9]|[0-1][0-9]|[2][0-3]))|([\?])|([\*]))[\s](((([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?,)*([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?(C)?)|(([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])/([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(C)?)|(L(-[0-9])?)|(L(-[1-2][0-9])?)|(L(-[3][0-1])?)|(LW)|([1-9]W)|([1-3][0-9]W)|([\?])|([\*]))[\s](((([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?,)*([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?)|(([1-9]|0[1-9]|1[0-2])/([1-9]|0[1-9]|1[0-2]))|(((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?,)*(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)|((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)/(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))|([\?])|([\*]))[\s]((([1-7](-([1-7]))?,)*([1-7])(-([1-7]))?)|([1-7]/([1-7]))|(((MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?,)*(MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?(C)?)|((MON|TUE|WED|THU|FRI|SAT|SUN)/(MON|TUE|WED|THU|FRI|SAT|SUN)(C)?)|(([1-7]|(MON|TUE|WED|THU|FRI|SAT|SUN))?(L|LW)?)|(([1-7]|MON|TUE|WED|THU|FRI|SAT|SUN)#([1-7])?)|([\?])|([\*]))([\s]?(([\*])?|(19[7-9][0-9])|(20[0-9][0-9]))?| (((19[7-9][0-9])|(20[0-9][0-9]))/((19[7-9][0-9])|(20[0-9][0-9])))?| ((((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?,)*((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?)?)

回答by kekec

You could use cron-utilsNot only will check the cron is valid, but you could convert from different cron formats to the target one (ex.: if the user inputs a Unix cron expression, you could easily convert to Quartz and persist that one to DB). Below we provide some snippets:

您可以使用cron-utils不仅会检查 cron 是否有效,而且您可以从不同的 cron 格式转换为目标格式(例如:如果用户输入 Unix cron 表达式,您可以轻松转换为 Quartz 并保留该表达式到数据库)。下面我们提供一些片段:

// Turn cron expressions into another format by using CronMapper:
CronMapper cronMapper = CronMapper.fromUnixToQuartz();

Cron quartzCron = cronMapper.map(unixCron);
// and to get a String representation of it, we can use
quartzCron.asString();

//validate the cron expression!
quartzCron.validate()

回答by LHCHIN

If you are using org.quartz, you can validate a Cron expression as follows:

如果您正在使用org.quartz,则可以按如下方式验证 Cron 表达式:

try {
    CronExpression cron = new CronExpression(cronExpression);
    ...
} catch (ParseException e) {
    //exception handling
}


Quoted from official API document Class CronExpression:

引自官方 API 文档Class CronExpression

public CronExpression(String cronExpression) throws ParseException
Constructs a new CronExpression based on the specified parameter.
Parameters:
cronExpression - String representation of the cron expression the new object should represent
Throws:
ParseException - if the string expression cannot be parsed into a valid CronExpression

public CronExpression(String cronExpression) throws ParseException
根据指定的参数构造一个新的 CronExpression。
参数:
cronExpression - 新对象应表示的 cron 表达式 的字符串表示
抛出:
ParseException - 如果无法将字符串表达式解析为有效的 CronExpression