cron expression is implemented in cycles of year, week, month, hour, minute and second

First, let's understand what cron expression is and the format of cron expression.   

Introduction to cron expression

What is a cron expression?

cron expression is a string with time meaning, which is generally used to define the execution time of a scheduled task.

Format of cron expression

{seconds} {minutes} {hours} {date} {month} {week} {year (can be empty)}

Allowable value of each field of cron expression

  • Seconds 0-59, - * /
  • Score 0-59, - * /
  • Hours 0-23, - * /
  • Date 1-31, - */ L W C 
  • Month 1-12 or JAN-DEC, - * /
  • Monday-7 or sun-sat, - */ L C # 
  • Year (optional) left blank, 1970-2099, - * /

Meaning of each symbol

*Represents all values;  
? Indicates an unspecified value, i.e. it does not care what it is;  
-Represents a specified range;  
, indicating that a possible value is added;  
/Before the symbol indicates the start time, and after the symbol indicates the value of each increment;  
L("last") ("last") "L" is used in the day of month field to mean "the last day of the month"; Used in the day of week field, it simply means "7" or "SAT". If used in conjunction with numbers in the day of week field, it means "the last day of the month" - for example, "6L" means "the last Friday of the month" When we use "L", it is important not to specify a list value or range, otherwise we will get some unexpected results.  
W("weekday") can only be used in the day of month field. Used to describe the working days (Monday to Friday) closest to the specified day. For example, "15W" in the day of month field refers to "the working day closest to the 15th day of this month". That is, if the 15th day of this month is Saturday, the trigger will be triggered on the 14th day of this month, that is, Friday; If the 15th day of this month is Sunday, the trigger will be triggered on the 16th day of this month, that is, Monday; If the 15th day of this month is Tuesday, it is triggered on the trigger day. Note: this usage will only calculate the value in the current month, and will not cross the current month. The "W" character can only indicate a day in the day of month, and cannot be a range or list. You can also use "LW" to specify the last working day of this month.  
#Can only be used in the day of week field. Used to specify the week ordinal of this month. For example, in the day of week field, "6 # 3" refers to the third Friday of the month (6 refers to Friday and 3 refers to the third). If the specified date does not exist, the trigger will not be triggered.  
C refers to the value calculated after contacting calendar. For example, "5C" in the day of month field refers to the first day of the calendar on or after the fifth day of the month; In the day of week field, "1C" refers to the first day including the calendar on or after this Sunday.

List of cron expressions

  (1)0/2 * * * * ? Indicates that the task is executed every 2 seconds

  (1)0 0/2 * * * ? Indicates that the task is executed every 2 minutes

  (1)0 0 2 1 * ? Indicates that the task is adjusted at 2 a.m. on the 1st of each month

  (2)0 15 10 ? * MON-FRI means to perform the work at 10:15 a.m. every day from Monday to Friday

  (3)0 15 10 ? 6L "2002-2006" refers to the last Friday of each month in 2002-2006, which is executed at 10:15 a.m

  (4)0 0 10,14,16 * * ? Every day at 10 a.m., 2 p.m. and 4 p.m

  (5)0 0/30 9-17 * * ? Every half an hour during working hours from 9 to 5

  (6)0 0 12 ? * WED means 12 noon every Wednesday

  (7)0 0 12 * * ? Triggered at 12 noon every day

  (8)0 15 10 ? * * Triggered at 10:15 every morning

  (9)0 15 10 * * ? Triggered at 10:15 every morning

  (10)0 15 10 * * ? Triggered at 10:15 every morning

  (11)0 15 10 * * ? 2005 triggered at 10:15 a.m. every day in 2005

  (12)0 * 14 * * ? Triggered every 1 minute from 2:00 pm to 2:59 pm every day

  (13)0 0/5 14 * * ? Triggered every 5 minutes between 2:00 pm and 2:55 pm every day

  (14)0 0/5 14,18 * * ? Triggered every 5 minutes between 2:00 p.m. and 2:55 p.m. and between 6:00 p.m. and 6:55 p.m

  (15)0 0-5 14 * * ? Triggered every 1 minute from 2:00 pm to 2:05 pm every day

  (16)0 10,44 14 ? (3) wed is triggered at 2:10 and 2:44 pm on Wednesday in March every year

  (17)0 15 10 ? * MON-FRI triggered at 10:15 am from Monday to Friday

  (18)0 15 10 15 * ? Triggered at 10:15 am on the 15th of each month

  (19)0 15 10 L * ? Triggered at 10:15 am on the last day of each month

  (20)0 15 10 ? * 6L triggered at 10:15 am on the last Friday of each month

  (21)0 15 10 ? * 6L 2002-2005: triggered at 10:15 am on the last Friday of each month from 2002 to 2005

  (22)0 15 10 ? * 6 # 3 triggered at 10:15 am on the third Friday of each month

Online generation tool for cron expression:/ https://cron.qqe2.com/

How to dynamically generate cron expression

In our actual project, there may be a need for the user to select a task to be executed regularly. At this time, the back-end developer needs to dynamically generate cron expressions according to the time set by the user.

Here, I will demonstrate how to generate cron expressions through Cron utils.

Introducing dependencies

A link to Cron utils is attached here https://github.com/jmrozanec/cron-utils , with usage examples

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

Related class

Plan execution definition class

package com.spring4.cron;


import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;

/**
 * @Author: chenxuebing
 * @Description: Plan execution definition
 * @Date: 2022/8/10 16:33
 */
public class PlanExecuteDefBO {

    private static final long serialVersionUID = 4334316357655011464L;

    /**
     * Cycle type minute: minute hour: hour; Day: day; Week: week; Month: month; quarter: season; Year: year
     */
    private String cycleType;

    /**
     * cron expression
     */
    private String cron;

    /**
     * Scheduled task id
     */
    private String jobId;

    /**
     * start time
     */
    private LocalDate startTime;

    /**
     * End time:
     */
    private LocalDate endTime;

    /**
     * Times in cycle
     */
    private Integer numberOfCycles;

    /**
     * Specify the days of the week
     */
    private List<Integer> weekDays;

    /**
     * Specify the days of a month
     */
    private List<Integer> monthDays;

    /**
     * Day of the week
     */
    private Integer dayOfWeek;

    /**
     * Week number
     */
    private Integer week;

    /**
     * Duplicate rule
     */
    private String repeatRule;

    /**
     * execution time
     */
    private LocalTime executionTime;

    public String getCycleType() {
        return cycleType;
    }

    public void setCycleType(String cycleType) {
        this.cycleType = cycleType;
    }

    public void setStartTime(LocalDate startTime) {
        this.startTime = startTime;
    }

    public void setEndTime(LocalDate endTime) {
        this.endTime = endTime;
    }

    public String getCron() {
        return cron;
    }

    public void setCron(String cron) {
        this.cron = cron;
    }

    public String getJobId() {
        return jobId;
    }

    public void setJobId(String jobId) {
        this.jobId = jobId;
    }

    public Integer getNumberOfCycles() {
        return numberOfCycles;
    }

    public void setNumberOfCycles(Integer numberOfCycles) {
        this.numberOfCycles = numberOfCycles;
    }

    public LocalDate getStartTime() {
        return startTime;
    }

    public LocalDate getEndTime() {
        return endTime;
    }

    public List<Integer> getWeekDays() {
        return weekDays;
    }

    public void setWeekDays(List<Integer> weekDays) {
        this.weekDays = weekDays;
    }

    public List<Integer> getMonthDays() {
        return monthDays;
    }

    public void setMonthDays(List<Integer> monthDays) {
        this.monthDays = monthDays;
    }

    public Integer getDayOfWeek() {
        return dayOfWeek;
    }

    public void setDayOfWeek(Integer dayOfWeek) {
        this.dayOfWeek = dayOfWeek;
    }

    public Integer getWeek() {
        return week;
    }

    public void setWeek(Integer week) {
        this.week = week;
    }

    public String getRepeatRule() {
        return repeatRule;
    }

    public void setRepeatRule(String repeatRule) {
        this.repeatRule = repeatRule;
    }

    public LocalTime getExecutionTime() {
        return executionTime;
    }

    public void setExecutionTime(LocalTime executionTime) {
        this.executionTime = executionTime;
    }
}

Enumeration class

Plan cycle type mapping

package com.spring4.cron;

/**
 * @Author: chenxuebing
 * @Description: QC plan cycle type mapping
 * @Date: 2022/8/4 14:11
 */
public enum PlanCycleTypeEnum {
    MINUTE("minute", "minute"),

    HOUR("hour", "hour"),

    DAY("day", "day"),

    WEEK("week", "week"),

    MONTH("month", "month"),

    QUARTER("quarter", "quarter"),

    YEAR("year", "year");


    /**
     * Cycle type
     */
    private String cycleType;

    /**
     * describe
     */
    private String description;

    PlanCycleTypeEnum(String cycleType, String description) {
        this.cycleType = cycleType;
        this.description = description;
    }

    public String getCycleType() {
        return cycleType;
    }

    public String getDescription() {
        return description;
    }
}

Duplicate rule mapping

Select monthly cycle execution to select a certain day of each month or the day of the week of the month

package com.spring4.cron;

/**
 * @Author: chenxuebing
 * @Description: Duplicate rule
 * @Date: 2022/8/18 14:11
 */
public enum RepeatRuleEnum {
    WEEK("week", "week"),

    DATE("date", "date");

    private String value;

    private String description;

    RepeatRuleEnum(String type, String description) {
        this.value = type;
        this.description = description;
    }

    public String getType() {
        return value;
    }

    public void setType(String type) {
        this.value = type;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

Week mapping

package com.spring4.cron;

/**
 * @Author: chenxuebing
 * @Description: Week mapping
 * @Date: 2022/8/12 10:31
 */
public enum WeekEnum {
    SUNDAY(1, "Sunday"),

    MONDAY(2, "Monday"),

    TUESDAY(3, "Tuesday"),

    WEDNESDAY(4, "Wednesday"),

    THURSDAY(5, "Thursday"),

    FRIDAY(6, "Friday"),

    SATURDAY(7, "Saturday");

    private Integer value;

    private String description;

    WeekEnum(Integer value, String description) {
        this.value = value;
        this.description = description;
    }

    public Integer getValue() {
        return value;
    }

    public void setValue(Integer value) {
        this.value = value;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

CronUtils

package com.spring4.cron;

import com.cronutils.builder.CronBuilder;
import com.cronutils.model.CronType;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.model.field.expression.FieldExpression;
import com.cronutils.model.field.expression.FieldExpressionFactory;
import com.cronutils.model.field.expression.On;
import com.cronutils.model.field.value.SpecialChar;

import java.time.LocalDate;
import java.time.LocalTime;
import java.util.*;

import static com.cronutils.model.field.expression.FieldExpression.always;
import static com.cronutils.model.field.expression.FieldExpression.questionMark;
import static com.cronutils.model.field.expression.FieldExpressionFactory.*;

public class CronUntils {
    /**
     * week
     */
    private static final List<Integer> WEEKS =  Arrays.asList(WeekEnum.SUNDAY.getValue(),
            WeekEnum.MONDAY.getValue(),
            WeekEnum.THURSDAY.getValue(),
            WeekEnum.WEDNESDAY.getValue(),
            WeekEnum.THURSDAY.getValue(),
            WeekEnum.FRIDAY.getValue(),
            WeekEnum.SATURDAY.getValue());

    private static CronBuilder cronBuilder;

    static {
        cronBuilder = CronBuilder.cron(CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ));
    }

    public static String createCron(PlanExecuteDefBO planExecuteDefBO) {
        LocalTime executionTime = planExecuteDefBO.getExecutionTime();
        LocalDate startTime = planExecuteDefBO.getStartTime();
        String cycleType = planExecuteDefBO.getCycleType();
        int minute = planExecuteDefBO.getExecutionTime().getMinute();
        int hour = executionTime.getHour();
        int day = startTime.getDayOfMonth();
        int month = startTime.getMonth().getValue();
        cronBuilder = cronBuilder.withSecond(on(executionTime.getSecond()));
        // Once per minute
        if (Objects.equals(PlanCycleTypeEnum.MINUTE.getCycleType(), cycleType)) {
            return cronBuilder.withDoW(questionMark())
                    .withMonth(always())
                    .withDoM(always())
                    .withHour(always())
                    .withMinute(always()).instance().asString();
        }

        // Once an hour
        if (Objects.equals(PlanCycleTypeEnum.HOUR.getCycleType(), cycleType)) {
            return cronBuilder.withDoW(questionMark())
                    .withMonth(always())
                    .withDoM(always())
                    .withHour(always())
                    .withMinute(on(executionTime.getMinute())).instance().asString();
        }

        // Once a day
        if (Objects.equals(PlanCycleTypeEnum.DAY.getCycleType(), cycleType)) {
            return cronBuilder.withDoW(questionMark())
                    .withMonth(always())
                    .withDoM(always())
                    .withHour(on(hour))
                    .withMinute(on(minute))
                    .instance().asString();
        }

        // Once a week
        if (Objects.equals(PlanCycleTypeEnum.WEEK.getCycleType(), cycleType)) {
            List<FieldExpression> weekDays = new ArrayList<>();
            planExecuteDefBO.getWeekDays().forEach(e -> weekDays.add(FieldExpressionFactory.on(e)));
            return cronBuilder.withDoW(and(weekDays))
                    .withMonth(always())
                    .withDoM(questionMark())
                    .withHour(on(hour))
                    .withMinute(on(minute))
                    .instance().asString();
        }

        // Once a month
        if (Objects.equals(PlanCycleTypeEnum.MONTH.getCycleType(), cycleType)) {
            List<FieldExpression> monthDays = new ArrayList<>();
            planExecuteDefBO.getMonthDays().forEach(e -> monthDays.add(FieldExpressionFactory.on(e)));
            if (Objects.equals(RepeatRuleEnum.DATE.getType(), planExecuteDefBO.getRepeatRule())) {
                return cronBuilder.withDoW(questionMark())
                        .withMonth(always())
                        .withDoM(and(monthDays))
                        .withHour(on(hour))
                        .withMinute(on(minute))
                        .instance().asString();
            }
            if (Objects.equals(RepeatRuleEnum.WEEK.getType(), planExecuteDefBO.getRepeatRule())) {
                return cronBuilder.withDoW(on(WEEKS.get(planExecuteDefBO.getDayOfWeek()), SpecialChar.HASH, planExecuteDefBO.getWeek()))
                        .withMonth(always())
                        .withDoM(questionMark())
                        .withHour(on(hour))
                        .withMinute(on(minute))
                        .instance().asString();
            }
        }

        // Once a quarter
        if (Objects.equals(PlanCycleTypeEnum.QUARTER.getCycleType(), cycleType)) {
            List<FieldExpression> flist = new ArrayList<>();
            On quarter1 = FieldExpressionFactory.on(1);
            On quarter2 = FieldExpressionFactory.on(4);
            On quarter3 = FieldExpressionFactory.on(7);
            On quarter4 = FieldExpressionFactory.on(10);
            flist.add(quarter1);
            flist.add(quarter2);
            flist.add(quarter3);
            flist.add(quarter4);
            return cronBuilder.withDoW(questionMark())
                    .withMonth(and(flist))
                    .withDoM(on(day))
                    .withHour(on(hour))
                    .withMinute(on(minute))
                    .instance().asString();
        }

        // once a year
        if (Objects.equals(PlanCycleTypeEnum.YEAR.getCycleType(), cycleType)) {
            List<FieldExpression> flist = new ArrayList<>();
            On on = FieldExpressionFactory.on(day);
            flist.add(on);
            return cronBuilder.withYear(always())
                    .withDoW(questionMark())
                    .withMonth(on(month))
                    .withDoM(on(day))
                    .withHour(on(hour))
                    .withMinute(on(minute))
                    .instance().asString();
        }

        // Execute in seconds
        return cronBuilder.withYear(always())
                .withDoW(questionMark())
                .withMonth(always())
                .withDoM(always())
                .withHour(always())
                .withMinute(always()).instance().asString();
    }
}

Test class

package com.spring4.cron;

import com.cronutils.builder.CronBuilder;
import com.cronutils.descriptor.CronDescriptor;
import com.cronutils.mapper.CronMapper;
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.model.field.expression.FieldExpression;
import com.cronutils.model.field.expression.FieldExpressionFactory;
import com.cronutils.model.field.expression.On;
import com.cronutils.model.field.value.SpecialChar;
import com.cronutils.model.time.ExecutionTime;
import com.cronutils.parser.CronParser;


import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;

import static com.cronutils.model.field.expression.FieldExpression.always;
import static com.cronutils.model.field.expression.FieldExpression.questionMark;
import static com.cronutils.model.field.expression.FieldExpressionFactory.*;

public class CronDemo {
    public static void main(String[] args) {
        CronDefinition = cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ);
        CronParser parser = new CronParser(cronDefinition);
        CronDescriptor descriptor = CronDescriptor.instance(Locale.UK);
        LocalDate startTime = LocalDate.of(2022, 9, 1);
        LocalTime executeTime = LocalTime.of(9, 0, 0);
        PlanExecuteDefBO planExecuteDefBO = new PlanExecuteDefBO();
        planExecuteDefBO.setStartTime(startTime);
        planExecuteDefBO.setExecutionTime(executeTime);
        planExecuteDefBO.setCycleType("minute");
        // Once per minute
        String minute = CronUntils.createCron(planExecuteDefBO);
        System.out.println("per minute:" + minute +  ":" + descriptor.describe(parser.parse(minute)));

        // Once every hour
        planExecuteDefBO.setCycleType("hour");
        String hour = CronUntils.createCron(planExecuteDefBO);
        System.out.println("Per hour:" + hour +  ":" +  descriptor.describe(parser.parse(hour)));

        // Once a day
        planExecuteDefBO.setCycleType("day");
        String day = CronUntils.createCron(planExecuteDefBO);
        System.out.println("Every day:" + day +  ":" +  descriptor.describe(parser.parse(day)));

        // Once a week
        planExecuteDefBO.setCycleType("week");
        planExecuteDefBO.setWeekDays(Arrays.asList(1,3));
        String week = CronUntils.createCron(planExecuteDefBO);
        System.out.println("weekly:" + week +  ":" +  descriptor.describe(parser.parse(week)));

        // Once a month
        planExecuteDefBO.setCycleType("month");
        planExecuteDefBO.setMonthDays(Arrays.asList(1,11,25));
        //planExecuteDefBO.setRepeatRule("date"); //  Execute on a certain day of the specified month
        planExecuteDefBO.setRepeatRule("week"); // Select the day of the week of the month
        planExecuteDefBO.setDayOfWeek(1);
        planExecuteDefBO.setWeek(1);
        String month = CronUntils.createCron(planExecuteDefBO);
        System.out.println("monthly:" + month +  ":" + descriptor.describe(parser.parse(month)));

        // Once a quarter
        planExecuteDefBO.setCycleType("quarter");
        String quarter = CronUntils.createCron(planExecuteDefBO);
        System.out.println("Quarterly:" + quarter +  ":" + descriptor.describe(parser.parse(quarter)));

        // Once a year
        planExecuteDefBO.setCycleType("year");
        String year = CronUntils.createCron(planExecuteDefBO);
        System.out.println("annually:" + year +  ":" + descriptor.describe(parser.parse(year)));


    }
}

Operation results

 

Posted by rocklv on Thu, 18 Aug 2022 12:40:10 +0530