mac_girl

cron表达式实现每年、每周、每月、每小时、每分、每秒周期执行

首先我们先来认识一下什么是cron表达式,cron表达式的格式。  

cron表达式的介绍

什么是cron表达式?

cron表达式是一个具有时间含义的字符串,一般用于定义定时任务的执行时间。

cron表达式的格式

{秒数}{分钟}{小时}{日期}{月份}{星期}{年份(可为空)}

cron表达式每个字段的允许值

  • 秒 0-59 , - * / 
  • 分 0-59 , - * / 
  • 小时 0-23 , - * / 
  • 日期 1-31 , - * ? / L W C 
  • 月份 1-12 或者 JAN-DEC , - * / 
  • 星期 1-7 或者 SUN-SAT , - * ? / L C # 
  • 年(可选) 留空, 1970-2099 , - * / 

每个符号的意义

* 表示所有值; 
? 表示未说明的值,即不关心它为何值; 
- 表示一个指定的范围; 
, 表示附加一个可能值; 
/ 符号前表示开始时间,符号后表示每次递增的值; 
L("last") ("last") "L" 用在day-of-month字段意思是 "这个月最后一天";用在 day-of-week字段, 它简单意思是 "7" or "SAT"。 如果在day-of-week字段里和数字联合使用,它的意思就是 "这个月的最后一个星期几" – 例如: "6L" means "这个月的最后一个星期五". 当我们用“L”时,不指明一个列表值或者范围是很重要的,不然的话,我们会得到一些意想不到的结果。 
W("weekday") 只能用在day-of-month字段。用来描叙最接近指定天的工作日(周一到周五)。例如:在day-of-month字段用“15W”指“最接近这个 月第15天的工作日”,即如果这个月第15天是周六,那么触发器将会在这个月第14天即周五触发;如果这个月第15天是周日,那么触发器将会在这个月第 16天即周一触发;如果这个月第15天是周二,那么就在触发器这天触发。注意一点:这个用法只会在当前月计算值,不会越过当前月。“W”字符仅能在 day-of-month指明一天,不能是一个范围或列表。也可以用“LW”来指定这个月的最后一个工作日。 
# 只能用在day-of-week字段。用来指定这个月的第几个周几。例:在day-of-week字段用"6#3"指这个月第3个周五(6指周五,3指第3个)。如果指定的日期不存在,触发器就不会触发。 
C 指和calendar联系后计算过的值。例:在day-of-month 字段用“5C”指在这个月第5天或之后包括calendar的第一天;在day-of-week字段用“1C”指在这周日或之后包括calendar的第一天。

cron表达式的案列

  (1)0/2 * * * * ?   表示每2秒 执行任务

  (1)0 0/2 * * * ?    表示每2分钟 执行任务

  (1)0 0 2 1 * ?   表示在每月的1日的凌晨2点调整任务

  (2)0 15 10 ? * MON-FRI   表示周一到周五每天上午10:15执行作业

  (3)0 15 10 ? 6L 2002-2006   表示2002-2006年的每个月的最后一个星期五上午10:15执行作

  (4)0 0 10,14,16 * * ?   每天上午10点,下午2点,4点 

  (5)0 0/30 9-17 * * ?   朝九晚五工作时间内每半小时 

  (6)0 0 12 ? * WED    表示每个星期三中午12点 

  (7)0 0 12 * * ?   每天中午12点触发 

  (8)0 15 10 ? * *    每天上午10:15触发 

  (9)0 15 10 * * ?     每天上午10:15触发 

  (10)0 15 10 * * ?    每天上午10:15触发 

  (11)0 15 10 * * ? 2005    2005年的每天上午10:15触发 

  (12)0 * 14 * * ?     在每天下午2点到下午2:59期间的每1分钟触发 

  (13)0 0/5 14 * * ?    在每天下午2点到下午2:55期间的每5分钟触发 

  (14)0 0/5 14,18 * * ?     在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 

  (15)0 0-5 14 * * ?    在每天下午2点到下午2:05期间的每1分钟触发 

  (16)0 10,44 14 ? 3 WED    每年三月的星期三的下午2:10和2:44触发 

  (17)0 15 10 ? * MON-FRI    周一至周五的上午10:15触发 

  (18)0 15 10 15 * ?    每月15日上午10:15触发 

  (19)0 15 10 L * ?    每月最后一日的上午10:15触发 

  (20)0 15 10 ? * 6L    每月的最后一个星期五上午10:15触发 

  (21)0 15 10 ? * 6L 2002-2005   2002年至2005年的每月的最后一个星期五上午10:15触发 

  (22)0 15 10 ? * 6#3   每月的第三个星期五上午10:15触发

cron表达式的在线生成工具:/https://cron.qqe2.com/

如何动态的生成cron表达式

在我我们实际的项目中,可能会出现这样的需求,由用户选择定时的执行一个任务,这个时候就需要后端开发人员根据用户设定的时间动态的生成cron表达式了。

在此,我来演示如何通过cron-utils生成cron表达式。

引入依赖

此处附有cron-utils的链接https://github.com/jmrozanec/cron-utils,内有使用示例

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

相关类

计划执行定义类

package com.spring4.cron;


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

/**
 * @Author: chenxuebing
 * @Description: 计划执行定义
 * @Date: 2022/8/10 16:33
 */
public class PlanExecuteDefBO {

    private static final long serialVersionUID = 4334316357655011464L;

    /**
     * 周期类型 minute:分钟 hour: 小时; day: 天; week: 周; month: 月; quarter: 季; year: 年
     */
    private String cycleType;

    /**
     * cron表达式
     */
    private String cron;

    /**
     * 定时任务id
     */
    private String jobId;

    /**
     * 开始时间
     */
    private LocalDate startTime;

    /**
     * 结束时间
     */
    private LocalDate endTime;

    /**
     * 周期内次数
     */
    private Integer numberOfCycles;

    /**
     * 指定一周哪几天
     */
    private List<Integer> weekDays;

    /**
     * 指定一个月哪几天
     */
    private List<Integer> monthDays;

    /**
     * 一周的星期几
     */
    private Integer dayOfWeek;

    /**
     * 第几周
     */
    private Integer week;

    /**
     * 重复规则
     */
    private String repeatRule;

    /**
     * 执行时间
     */
    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;
    }
}

枚举类

计划周期类型映射

package com.spring4.cron;

/**
 * @Author: chenxuebing
 * @Description: 质控计划周期类型映射
 * @Date: 2022/8/4 14:11
 */
public enum PlanCycleTypeEnum {
    MINUTE("minute", "分钟"),

    HOUR("hour", "小时"),

    DAY("day", "日"),

    WEEK("week", "周"),

    MONTH("month", "月"),

    QUARTER("quarter", "季度"),

    YEAR("year", "年");


    /**
     * 周期类型
     */
    private String cycleType;

    /**
     * 描述
     */
    private String description;

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

    public String getCycleType() {
        return cycleType;
    }

    public String getDescription() {
        return description;
    }
}

重复规则映射

选择每月周期执行可选择每月某几天执行或每月的第几周的星期几执行

package com.spring4.cron;

/**
 * @Author: chenxuebing
 * @Description: 重复规则
 * @Date: 2022/8/18 14:11
 */
public enum RepeatRuleEnum {
    WEEK("week", "周"),

    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;
    }
}

星期映射

package com.spring4.cron;

/**
 * @Author: chenxuebing
 * @Description: 星期映射
 * @Date: 2022/8/12 10:31
 */
public enum WeekEnum {
    SUNDAY(1, "星期天"),

    MONDAY(2, "星期一"),

    TUESDAY(3, "星期二"),

    WEDNESDAY(4, "星期三"),

    THURSDAY(5, "星期四"),

    FRIDAY(6, "星期五"),

    SATURDAY(7, "星期六");

    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 {
    /**
     * 星期
     */
    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()));
        // 每分钟一次
        if (Objects.equals(PlanCycleTypeEnum.MINUTE.getCycleType(), cycleType)) {
            return cronBuilder.withDoW(questionMark())
                    .withMonth(always())
                    .withDoM(always())
                    .withHour(always())
                    .withMinute(always()).instance().asString();
        }

        // 每小时一次
        if (Objects.equals(PlanCycleTypeEnum.HOUR.getCycleType(), cycleType)) {
            return cronBuilder.withDoW(questionMark())
                    .withMonth(always())
                    .withDoM(always())
                    .withHour(always())
                    .withMinute(on(executionTime.getMinute())).instance().asString();
        }

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

        // 每周一次
        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();
        }

        // 每月一次
        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();
            }
        }

        // 每季度一次
        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();
        }

        // 每年一次
        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();
        }

        // 按秒执行
        return cronBuilder.withYear(always())
                .withDoW(questionMark())
                .withMonth(always())
                .withDoM(always())
                .withHour(always())
                .withMinute(always()).instance().asString();
    }
}

测试类

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");
        // 每分钟执行一次
        String minute = CronUntils.createCron(planExecuteDefBO);
        System.out.println("每分钟:" + minute +  ":" + descriptor.describe(parser.parse(minute)));

        // 每小时执行一次
        planExecuteDefBO.setCycleType("hour");
        String hour = CronUntils.createCron(planExecuteDefBO);
        System.out.println("每小时:" + hour +  ":" +  descriptor.describe(parser.parse(hour)));

        // 每天执行一次
        planExecuteDefBO.setCycleType("day");
        String day = CronUntils.createCron(planExecuteDefBO);
        System.out.println("每天:" + day +  ":" +  descriptor.describe(parser.parse(day)));

        // 每周执行一次
        planExecuteDefBO.setCycleType("week");
        planExecuteDefBO.setWeekDays(Arrays.asList(1,3));
        String week = CronUntils.createCron(planExecuteDefBO);
        System.out.println("每周:" + week +  ":" +  descriptor.describe(parser.parse(week)));

        // 每月执行一次
        planExecuteDefBO.setCycleType("month");
        planExecuteDefBO.setMonthDays(Arrays.asList(1,11,25));
        //planExecuteDefBO.setRepeatRule("date"); // 指定月内某几天执行
        planExecuteDefBO.setRepeatRule("week"); // 可选每月第几周的星期几执行
        planExecuteDefBO.setDayOfWeek(1);
        planExecuteDefBO.setWeek(1);
        String month = CronUntils.createCron(planExecuteDefBO);
        System.out.println("每月:" + month +  ":" + descriptor.describe(parser.parse(month)));

        // 每季度执行一次
        planExecuteDefBO.setCycleType("quarter");
        String quarter = CronUntils.createCron(planExecuteDefBO);
        System.out.println("每季度:" + quarter +  ":" + descriptor.describe(parser.parse(quarter)));

        // 每年执行一次
        planExecuteDefBO.setCycleType("year");
        String year = CronUntils.createCron(planExecuteDefBO);
        System.out.println("每年:" + year +  ":" + descriptor.describe(parser.parse(year)));


    }
}

运行结果

 

posted on 2022-08-18 16:21  宇宙美少女  阅读(31067)  评论(0编辑  收藏  举报

导航