精华内容
下载资源
问答
  • cron表达式每天整点执行一次的问题

    万次阅读 2020-11-13 10:21:42
    最近写了个发短信的定时任务,需求是每天上午10发信息,然后我百度了一篇文章,复制了一个cron表达式:* * 10 * * ?。然后隔天去看日志,发现,从100分0秒开始,每秒执行一次, 执行了3600次。然后才发现cron...

    最近写了个发短信的定时任务,需求是每天上午10点发信息,然后我百度了一篇文章,复制了一个cron表达式:* * 10 * * ?。然后隔天去看日志,发现,从10点0分0秒开始,每秒执行一次, 执行了3600次。然后才发现cron表达式配置错了,应该是 0 0 10 * * ?,这才是每天上午10点执行一次。幸亏是没有数据可以发短息,不然我要跑路了。

    切记:如果只是执行一次,要把表达式前边的*换成0.

    展开全文
  • 一个基本的想法是,在任务执行前保存一条记录,任务执行后更新此记录的结束时间和标记,异常的时候记录失败标记和异常信息,然后管理员每天登录的时候检查每个任务是否正常执行。如果记录与设置的执行时刻匹配,...

          参见 http://gitee.com/xxssyyyyssxx/cron-hms

          我们项目中一般会有很多的定时任务,我们怎么知道这些定时任务是否正常执行了呢?一个基本的想法是,在任务执行前保存一条记录,任务执行后更新此记录的结束时间和标记,异常的时候记录失败标记和异常信息,然后管理员每天登录的时候检查每个任务是否正常执行。如果记录与设置的执行时刻点匹配,说明任务正常执行了,记录不存在或者有失败标记说明任务未正常执行。此谓任务监控。

           定时任务一般地由Quartz或者Spring实现,他们都通过一个cron表达式来指定在什么时刻执行。对于Quartz可以用Aspectj来拦截job,前后记录;对于Spring的Scheduled,可以用AOP拦截@Scheduled注解或者自定义一个注解,前后记录。

           现在的关键问题来了,如何根据cron表达式计算每天的哪些时候执行或者该不该执行?

           我们知道,一般地,cron表达式有六个域,分别代表秒、分、时、日、月、星期。每个域可能出现*、/、,、-、数字等。为了模型简单化,解析每个域即可知道每个域的哪些点应该执行,那么一天中,秒、分、时的笛卡尔积即是每天的执行点。任意给定的日期,我们就能知道这个日期应不应该执行(判断星期、月、日),哪些时刻执行(秒、分、时的笛卡尔积)?

           talk is cheap,show me the code。

    思路:

    1、切割cron表达式
    2、转换每个域
    3、计算执行时间点(关键算法,解析cron表达式)
    4、计算某一天的哪些时间点执行

          首先设计表达每个域的枚举,可以指定位置参数、最小值、最大值。提供根据位置计算枚举域的静态方法。再聚合一个JavaBean表示一个域。

    /**
     * cron表达式某个位置上的一些常量
     * @author xiongshiyan at 2018/11/17 , contact me with email yanshixiong@126.com or phone 15208384257
     */
    public enum  CronPosition {
        SECOND(0 , 0 , 59) ,
        MINUTE(1 , 0 , 59) ,
        HOUR  (2 , 0 , 23) ,
        DAY   (3 , 1 , 31) ,
        MONTH (4 , 1 , 12) ,
        WEEK  (5 , 0 , 6)  ,
        YEAR  (6 , 2018 , 2099);
        /**
         * 在cron表达式中的位置
         */
        private int position;
        /**
         * 该域最小值
         */
        private Integer min;
        /**
         * 该域最大值
         */
        private Integer max;
    
        CronPosition(int position , Integer min , Integer max){
            this.position = position;
            this.min = min;
            this.max = max;
        }
    
        public int getPosition() {
            return position;
        }
    
        public Integer getMin() {
            return min;
        }
    
        public Integer getMax() {
            return max;
        }
    
        public static CronPosition fromPosition(int position){
            CronPosition[] values = CronPosition.values();
            for (CronPosition field : values) {
                if(position == field.position){
                    return field;
                }
            }
            return null;
        }
    }
    

          然后就是切割cron表达式、转换每个域、计算执行时间点(关键算法,解析cron表达式)、计算某一天的哪些时间点执行。

       

    /**
         * 1.把cron表达式切成域表达式
         *
         * @param cron cron
         * @return 代表每个域的列表
         */
        public static List<String> cut(String cron) {
            cron = cron.trim();
            String[] split = cron.split(CRON_CUT);
            return Arrays.asList(split);
        }
    /**
         * 2.cron域表达式转换为域
         */
        public static List<CronField> convertCronField(String cron) {
            List<String> cut = cut(cron);
            int size = cut.size();
            if (CRON_LEN != size && (CRON_LEN + 1) != size) {
                throw new IllegalArgumentException("cron表达式必须有六个域或者七个域(最后为年)");
            }
            List<CronField> cronFields = new ArrayList<>(size);
            for (int i = 0; i < size; i++) {
                CronPosition cronPosition = CronPosition.fromPosition(i);
                cronFields.add(new CronField(
                        cronPosition,
                        CronShapingUtil.shaping(cut.get(i), cronPosition)
                ));
            }
            return cronFields;
        }

    CronField是表达一个域的,保存域相关信息,并提供计算执行点的方法。

    package top.jfunc.cron.pojo;
    
    import top.jfunc.cron.util.CompareUtil;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    /**
     * cron表达式的域
     * @author xiongshiyan
     */
    public class CronField {
        public static final String STAR = "*";
        public static final String COMMA = ",";
        public static final String HYPHEN = "-";
        public static final String SLASH = "/";
    
        private CronPosition cronPosition;
        private String express;
        private List<Integer> listCache = null;
    
        public CronField(CronPosition cronPosition, String express) {
            this.cronPosition = cronPosition;
            this.express = express;
        }
    
        public CronPosition getCronPosition() {
            return cronPosition;
        }
    
        public void setCronPosition(CronPosition cronPosition) {
            this.cronPosition = cronPosition;
        }
    
        public String getExpress() {
            return express;
        }
    
        public void setExpress(String express) {
            this.express = express;
        }
    
        /**
         * 是否包含全部的数值
         */
        public boolean containsAll(){
            return STAR.equals(getExpress());
        }
    
        /**
         * 3.计算某域的哪些点
         */
        public List<Integer> points() {
            //缓存计算的
            if(null != listCache){
                return listCache;
            }
    
            listCache = new ArrayList<>(5);
    
            String express = this.getExpress();
            CronPosition cronPosition = this.getCronPosition();
            Integer min = cronPosition.getMin();
            Integer max = cronPosition.getMax();
    
            // *这种情况
            if (STAR.equals(express)) {
                for (int i = min; i <= max; i++) {
                    listCache.add(i);
                }
                return listCache;
            }
            // 带有,的情况,分割之后每部分单独处理
            if (express.contains(COMMA)) {
                String[] split = express.split(COMMA);
                for (String part : split) {
                    listCache.addAll( new CronField(this.getCronPosition(), part).points());
                }
                if (listCache.size() > 1) {
                    //去重
                    CompareUtil.removeDuplicate(listCache);
                    //排序
                    Collections.sort(listCache);
                }
    
                return listCache;
            }
            // 0-3 0/2 3-15/2 5  模式统一为 (min-max)/step
            Integer left;
            Integer right;
            Integer step = 1;
    
            //包含-的情况
            if (express.contains(HYPHEN)) {
                String[] strings = express.split(HYPHEN);
                left = Integer.valueOf(strings[0]);
                CompareUtil.assertRange(cronPosition, left);
                //1-32/2的情况
                if (strings[1].contains(SLASH)) {
                    String[] split = strings[1].split(SLASH);
                    //32
                    right = Integer.valueOf(split[0]);
                    CompareUtil.assertSize(left, right);
                    CompareUtil.assertRange(cronPosition, right);
                    //2
                    step = Integer.valueOf(split[1]);
                } else {
                    //1-32的情况
                    right = Integer.valueOf(strings[1]);
                    CompareUtil.assertSize(left, right);
                    CompareUtil.assertRange(cronPosition, right);
                }
                //仅仅包含/
            } else if (express.contains(SLASH)) {
                String[] strings = express.split(SLASH);
                left = Integer.valueOf(strings[0]);
                CompareUtil.assertRange(cronPosition, left);
                step = Integer.valueOf(strings[1]);
                right = max;
                CompareUtil.assertSize(left, right);
            } else {
                // 普通的数字
                Integer single = Integer.valueOf(express);
                //星期域上 7 转换为 0
                if(CronPosition.WEEK == this.cronPosition && 7 == single){
                    single = 0;
                }
                CompareUtil.assertRange(cronPosition, single);
                listCache.add(single);
                return listCache;
            }
    
            for (int i = left; i <= right; i += step) {
                listCache.add(i);
            }
            return listCache;
    
        }
    
        @Override
        public String toString() {
            return "CronField{" +
                    "cronPosition=" + cronPosition +
                    ", express='" + express + '\'' +
                    '}';
        }
    }
    

    然后计算某一天的哪些时刻点执行。

    /**
         * 4.计算cron表达式在某一天的那些时间执行,精确到秒
         * 秒 分 时 日 月 周 (年)
         * "0 15 10 ? * *"  每天上午10:15触发
         * "0 0/5 14 * * ?"  在每天下午2点到下午2:55期间的每5分钟触发
         * "0 0-5 14 * * ?"  每天下午2点到下午2:05期间的每1分钟触发
         * "0 10,44 14 ? 3 WED"  三月的星期三的下午2:10和2:44触发
         * "0 15 10 ? * MON-FRI"  周一至周五的上午10:15触发
         * ---------------------
         *
         * @param cron cron表达式
         * @param date 时间,某天
         * @return 这一天的哪些时分秒执行, 不执行的返回空
         */
        public static List<TimeOfDay> calculate(String cron, Date date) {
            List<CronField> cronFields = convertCronField(cron);
            int year = DateUtil.year(date);
            int week = DateUtil.week(date);
            int month = DateUtil.month(date);
            int day = DateUtil.day(date);
            /// 如果包含年域
            if (CRON_LEN_YEAR == cronFields.size()) {
                CronField fieldYear = cronFields.get(CronPosition.YEAR.getPosition());
                if (!assertExecute(year, fieldYear, fieldYear.points())) {
                    return Collections.emptyList();
                }
            }
    
            CronField fieldWeek = cronFields.get(CronPosition.WEEK.getPosition());
            List<Integer> listWeek = fieldWeek.points();
            CronField fieldMonth = cronFields.get(CronPosition.MONTH.getPosition());
            List<Integer> listMonth = fieldMonth.points();
            CronField fieldDay = cronFields.get(CronPosition.DAY.getPosition());
            List<Integer> listDay = fieldDay.points();
            ///今天不执行就直接返回空
            if (!assertExecute(week, fieldWeek, listWeek)
                    || !assertExecute(month, fieldMonth, listMonth)
                    || !assertExecute(day, fieldDay, listDay)) {
                return Collections.emptyList();
            }
    
            CronField fieldHour = cronFields.get(CronPosition.HOUR.getPosition());
            List<Integer> listHour = fieldHour.points();
            CronField fieldMinute = cronFields.get(CronPosition.MINUTE.getPosition());
            List<Integer> listMinute = fieldMinute.points();
            CronField fieldSecond = cronFields.get(CronPosition.SECOND.getPosition());
            List<Integer> listSecond = fieldSecond.points();
    
            List<TimeOfDay> points = new ArrayList<>(listHour.size() * listMinute.size() * listSecond.size());
            for (Integer hour : listHour) {
                for (Integer minute : listMinute) {
                    for (Integer second : listSecond) {
                        points.add(new TimeOfDay(hour, minute, second));
                    }
                }
            }
            return points;
        }
    
        private static boolean assertExecute(int num, CronField cronField, List<Integer> list) {
            return CronField.STAR.equals(cronField.getExpress()) || CompareUtil.inList(num, list);
        }

    一些辅助工具类。替换?JAN-DEC、SUN-SAT等字符串的整形工具类。

    package top.jfunc.cron.util;
    
    import top.jfunc.cron.pojo.CronPosition;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    
    /**
     * @author xiongshiyan at 2018/10/12 , contact me with email yanshixiong@126.com or phone 15208384257
     */
    public class CronShapingUtil {
        private static final Map<String , String> MONTH_MAP = new HashMap<>(12);
        private static final Map<String , String> WEEK_MAP  = new HashMap<>(7);
        static {
            MONTH_MAP.put("JAN" , "1");
            MONTH_MAP.put("FEB" , "2");
            MONTH_MAP.put("MAR" , "3");
            MONTH_MAP.put("APR" , "4");
            MONTH_MAP.put("May" , "5");
            MONTH_MAP.put("JUN" , "6");
            MONTH_MAP.put("JUL" , "7");
            MONTH_MAP.put("AUG" , "8");
            MONTH_MAP.put("SEP" , "9");
            MONTH_MAP.put("OCT" , "10");
            MONTH_MAP.put("NOV" , "11");
            MONTH_MAP.put("DEC" , "12");
    
            WEEK_MAP.put("SUN" , "0");
            WEEK_MAP.put("MON" , "1");
            WEEK_MAP.put("TUE" , "2");
            WEEK_MAP.put("WED" , "3");
            WEEK_MAP.put("THU" , "4");
            WEEK_MAP.put("FRI" , "5");
            WEEK_MAP.put("SAT" , "6");
        }
    
        /**
         * 域整形,把某些英文字符串像JAN/SUN等转换为相应的数字
         */
        public static String shaping(String express , CronPosition cronPosition){
            if(cronPosition == CronPosition.MONTH){
                express = shapingMonth(express);
            }
    
            if(cronPosition == CronPosition.WEEK){
                express = shapingWeek(express);
                express = express.replace("?" , "*");
            }
    
            if(cronPosition == CronPosition.DAY){
                express = express.replace("?" , "*");
            }
            return express;
        }
    
        private static String shapingMonth(String express){
            Set<Map.Entry<String, String>> entrySet = MONTH_MAP.entrySet();
            for (Map.Entry<String, String> entry : entrySet) {
                if(express.toUpperCase().contains(entry.getKey())){
                    express = express.toUpperCase().replace(entry.getKey() , entry.getValue());
                }
            }
            return express;
        }
    
        private static String shapingWeek(String express){
            Set<Map.Entry<String, String>> entrySet = WEEK_MAP.entrySet();
            for (Map.Entry<String, String> entry : entrySet) {
                if(express.toUpperCase().contains(entry.getKey())){
                    express = express.toUpperCase().replace(entry.getKey() , entry.getValue());
                }
            }
            return express;
        }
    }
    

    时间计算等工具类。

    package top.jfunc.cron.util;
    
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Calendar;
    import java.util.Date;
    
    /**
     * @author xiongshiyan at 2018/11/18 , contact me with email yanshixiong@126.com or phone 15208384257
     */
    public class DateUtil {
        public static final String SDF_DATETIME       = "yyyy-MM-dd HH:mm:ss";
        public static final String SDF_DATETIME_SHORT = "yyyyMMddHHmmss";
        public static final String SDF_DATETIME_MS    = "yyyyMMddHHmmssSSS";
        public static final String SDF_DATE           = "yyyy-MM-dd";
    
        /**
         * 字符串转日期
         * @param dateStr 日期字符串
         * @return 日期 yyyy-MM-dd HH:mm:ss
         */
        public static Date toDate(String dateStr) {
            return toDate(dateStr, null);
        }
    
        /**
         * 日期转字符串
         * @param date 日期
         * @return 字符串 yyyy-MM-dd HH:mm:ss
         */
        public static String toStr(Date date) {
            return toStr(date, SDF_DATETIME);
        }
    
        /**
         * 日期转字符串
         * @param date 日期
         * @param format 格式化字符串
         * @return 字符串
         */
        public static String toStr(Date date, String format) {
            SimpleDateFormat sdf = null;
            if (null != format && !"".equals(format)) {
                sdf = new SimpleDateFormat(format);
                return sdf.format(date);
            } else {
                sdf = new SimpleDateFormat(SDF_DATETIME);
                return sdf.format(date);
            }
        }
    
        /**
         * 字符串转日期
         * @param dateStr 日期字符串
         * @param pattern 格式化字符串
         * @return 日期
         */
        public static Date toDate(String dateStr, String pattern) {
            try {
                if (null != pattern && !"".equals(pattern)) {
                    return new SimpleDateFormat(pattern).parse(dateStr);
                } else {
                    return new SimpleDateFormat(SDF_DATETIME).parse(dateStr);
                }
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
        }
        /**
         * 计算某一天是一个月的哪一天
         * @param date 日期
         * @return 1-31
         */
        public static int day(Date date){
            Calendar cal = Calendar.getInstance();
            cal.setTime(date);
            return cal.get(Calendar.DAY_OF_MONTH);
        }
        /**
         * 计算某一天是星期几
         * @param date 日期
         * @return 星期几,星期1是1,星期天是0  0-6
         */
        public static int week(Date date){
            Calendar cal = Calendar.getInstance();
            cal.setTime(date);
            return cal.get(Calendar.DAY_OF_WEEK) - 1;
        }
        /**
         * 计算某一天的月份
         * @param date 日期
         * @return 月份,1开始
         */
        public static int month(Date date){
            Calendar cal = Calendar.getInstance();
            cal.setTime(date);
            return cal.get(Calendar.MONTH) + 1;
        }
        /**
         * 计算某一天的年
         * @param date 日期
         * @return 年
         */
        public static int year(Date date){
            Calendar cal = Calendar.getInstance();
            cal.setTime(date);
            return cal.get(Calendar.YEAR);
        }
        /**
         * 计算某一天的时
         * @param date 日期
         * @return 时 0-23
         */
        public static int hour(Date date){
            Calendar cal = Calendar.getInstance();
            cal.setTime(date);
            return cal.get(Calendar.HOUR_OF_DAY);
        }
        /**
         * 计算某一天的分
         * @param date 日期
         * @return 秒 0-59
         */
        public static int minute(Date date){
            Calendar cal = Calendar.getInstance();
            cal.setTime(date);
            return cal.get(Calendar.MINUTE);
        }
        /**
         * 计算某一天的秒
         * @param date 日期
         * @return 秒 0-59
         */
        public static int second(Date date){
            Calendar cal = Calendar.getInstance();
            cal.setTime(date);
            return cal.get(Calendar.SECOND);
        }
    }
    

    除开解析L、W、C等,至此完成。

    https://blog.csdn.net/weixin_40426638/article/details/78959972

    https://blog.csdn.net/dabing69221/article/details/19012581

    https://blog.csdn.net/ukulelepku/article/details/54310035

    项目地址见:

    https://gitee.com/xxssyyyyssxx/cron-hms

     

    单元测试:

    package top.jfunc.cron;
    
    import org.junit.Assert;
    import org.junit.Test;
    import top.jfunc.cron.pojo.CronField;
    import top.jfunc.cron.pojo.TimeOfDay;
    import top.jfunc.cron.util.CronUtil;
    import top.jfunc.cron.util.DateUtil;
    
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Date;
    import java.util.List;
    
    /**
     * @author xiongshiyan at 2018/11/17 , contact me with email yanshixiong@126.com or phone 15208384257
     */
    public class CronTest {
        @Test
        public void testCalculate() {
            Date date = DateUtil.toDate("2018-11-17 12:00:12");
            Assert.assertEquals(2018, DateUtil.year(date));
            Assert.assertEquals(11, DateUtil.month(date));
            Assert.assertEquals(6, DateUtil.week(date));
            Assert.assertEquals(17, DateUtil.day(date));
            Assert.assertEquals(12, DateUtil.hour(date));
            Assert.assertEquals(0, DateUtil.minute(date));
            Assert.assertEquals(12, DateUtil.second(date));
    
            date = DateUtil.toDate("2018-11-18 12:00:12");
            Assert.assertEquals(0, DateUtil.week(date));
        }
    
        @Test
        public void testCut() {
            String cron = "0     15    10   ? *    *   ";
            Assert.assertEquals(
                    Arrays.asList("0", "15", "10", "?", "*", "*"),
                    CronUtil.cut(cron));
        }
    
        @Test
        public void testConvertField() {
            List<CronField> cronFields = CronUtil.convertCronField("0 0-5 14/2 * * ?");
            for (int i = 0; i < 6; i++) {
                CronField field = cronFields.get(i);
                Assert.assertEquals(field.getCronPosition().getPosition(), i);
            }
            Assert.assertEquals("0" , cronFields.get(0).getExpress());
            Assert.assertEquals("0-5" , cronFields.get(1).getExpress());
            Assert.assertEquals("14/2" , cronFields.get(2).getExpress());
            Assert.assertEquals("*" , cronFields.get(3).getExpress());
            Assert.assertEquals("*" , cronFields.get(4).getExpress());
            Assert.assertEquals("*" , cronFields.get(5).getExpress());
            List<CronField> fields = CronUtil.convertCronField("0 15 10 ? JAN-NOV MON-FRI");
            for (int i = 0; i < 6; i++) {
                CronField field = fields.get(i);
                Assert.assertEquals(field.getCronPosition().getPosition(), i);
            }
            Assert.assertEquals("0" , fields.get(0).getExpress());
            Assert.assertEquals("15" , fields.get(1).getExpress());
            Assert.assertEquals("10" , fields.get(2).getExpress());
            Assert.assertEquals("*" , fields.get(3).getExpress());
            Assert.assertEquals("1-11" , fields.get(4).getExpress());
            Assert.assertEquals("1-5" , fields.get(5).getExpress());
    
    
            //包含年域的情况
            cronFields = CronUtil.convertCronField("0 15 10 ? JAN-NOV MON-FRi 2018");
            System.out.println(cronFields);
        }
        @Test
        public void testConvertCronField(){
            List<CronField> cronFields = CronUtil.convertCronField("1 0-5 1/3 1,3,4 1-11/2 ?");
            Assert.assertEquals(Collections.singletonList(1) , cronFields.get(0).points());
            Assert.assertEquals(Arrays.asList(0,1,2,3,4,5) , cronFields.get(1).points());
            Assert.assertEquals(Arrays.asList(1,4,7,10,13,16,19,22) , cronFields.get(2).points());
            Assert.assertEquals(Arrays.asList(1,3,4) , cronFields.get(3).points());
            Assert.assertEquals(Arrays.asList(1,3,5,7,9,11) , cronFields.get(4).points());
            Assert.assertEquals(Arrays.asList(0,1,2,3,4,5,6) , cronFields.get(5).points());
        }
    
        @Test
        public void testCal(){
            Date date = DateUtil.toDate("2018-11-18 12:00:12");
            List<TimeOfDay> calculate = CronUtil.calculate("0 15 10 ? * *", date);
            Assert.assertEquals(Collections.singletonList(new TimeOfDay(10 , 15 , 0)) , calculate);
    
            calculate = CronUtil.calculate("0-5 15 10 ? * *", date);
            Assert.assertEquals(Arrays.asList(
                    new TimeOfDay(10 , 15 , 0),
                    new TimeOfDay(10 , 15 , 1),
                    new TimeOfDay(10 , 15 , 2),
                    new TimeOfDay(10 , 15 , 3),
                    new TimeOfDay(10 , 15 , 4),
                    new TimeOfDay(10 , 15 , 5)) , calculate);
    
            calculate = CronUtil.calculate("0 1/10 10 ? * *", date);
            Assert.assertEquals(Arrays.asList(
                    new TimeOfDay(10 , 1 , 0),
                    new TimeOfDay(10 , 11 , 0),
                    new TimeOfDay(10 , 21 , 0),
                    new TimeOfDay(10 , 31 , 0),
                    new TimeOfDay(10 , 41 , 0),
                    new TimeOfDay(10 , 51 , 0)) , calculate);
    
            calculate = CronUtil.calculate("0 1,4,6,8,10,50 10 ? * *", date);
            Assert.assertEquals(Arrays.asList(
                    new TimeOfDay(10 , 1 , 0),
                    new TimeOfDay(10 , 4 , 0),
                    new TimeOfDay(10 , 6 , 0),
                    new TimeOfDay(10 , 8 , 0),
                    new TimeOfDay(10 , 10 , 0),
                    new TimeOfDay(10 , 50 , 0)) , calculate);
    
            calculate = CronUtil.calculate("0 1-30/5 10 ? * *", date);
            Assert.assertEquals(Arrays.asList(
                    new TimeOfDay(10 , 1 , 0),
                    new TimeOfDay(10 , 6 , 0),
                    new TimeOfDay(10 , 11 , 0),
                    new TimeOfDay(10 , 16 , 0),
                    new TimeOfDay(10 , 21 , 0),
                    new TimeOfDay(10 , 26 , 0)) , calculate);
    
            calculate = CronUtil.calculate("0 1-30/5 10 ? * SUN", date);
            Assert.assertEquals(Arrays.asList(
                    new TimeOfDay(10 , 1 , 0),
                    new TimeOfDay(10 , 6 , 0),
                    new TimeOfDay(10 , 11 , 0),
                    new TimeOfDay(10 , 16 , 0),
                    new TimeOfDay(10 , 21 , 0),
                    new TimeOfDay(10 , 26 , 0)) , calculate);
    
            calculate = CronUtil.calculate("0 1-30/5 10 ? 11 *", date);
            Assert.assertEquals(Arrays.asList(
                    new TimeOfDay(10 , 1 , 0),
                    new TimeOfDay(10 , 6 , 0),
                    new TimeOfDay(10 , 11 , 0),
                    new TimeOfDay(10 , 16 , 0),
                    new TimeOfDay(10 , 21 , 0),
                    new TimeOfDay(10 , 26 , 0)) , calculate);
    
            calculate = CronUtil.calculate("0 1-4,43 10 ? 11 *", date);
            Assert.assertEquals(Arrays.asList(
                    new TimeOfDay(10 , 1 , 0),
                    new TimeOfDay(10 , 2 , 0),
                    new TimeOfDay(10 , 3 , 0),
                    new TimeOfDay(10 , 4 , 0),
                    new TimeOfDay(10 , 43 , 0)) , calculate);
    
            calculate = CronUtil.calculate("0 1-10/2,43 10 ? 11 *", date);
            Assert.assertEquals(Arrays.asList(
                    new TimeOfDay(10 , 1 , 0),
                    new TimeOfDay(10 , 3 , 0),
                    new TimeOfDay(10 , 5 , 0),
                    new TimeOfDay(10 , 7 , 0),
                    new TimeOfDay(10 , 9 , 0),
                    new TimeOfDay(10 , 43 , 0)) , calculate);
    
            calculate = CronUtil.calculate("0 7,1-5/2,5,6 10 ? 11 *", date);
            Assert.assertEquals(Arrays.asList(
                    new TimeOfDay(10 , 1 , 0),
                    new TimeOfDay(10 , 3 , 0),
                    new TimeOfDay(10 , 5 , 0),
                    new TimeOfDay(10 , 6 , 0),
                    new TimeOfDay(10 , 7 , 0)) , calculate);
    
            calculate = CronUtil.calculate("0 1-6/2,12-27/5 10 ? 11 *", date);
            Assert.assertEquals(Arrays.asList(
                    new TimeOfDay(10 , 1 , 0),
                    new TimeOfDay(10 , 3 , 0),
                    new TimeOfDay(10 , 5 , 0),
                    new TimeOfDay(10 , 12 , 0),
                    new TimeOfDay(10 , 17 , 0),
                    new TimeOfDay(10 , 22 , 0),
                    new TimeOfDay(10 , 27 , 0)) , calculate);
    
            ///星期一到星期六执行,星期天不执行就返回空集合
            calculate = CronUtil.calculate("0 1-30/5 10 ? * MON-SAT", date);
            Assert.assertEquals(Collections.emptyList(), calculate);
        }
        @Test(expected = IllegalArgumentException.class)
        public void testException1(){
            Date date = DateUtil.toDate("2018-11-18 12:00:12");
            CronUtil.calculate("5-0 15 10 ? * *", date);
        }
        @Test(expected = IllegalArgumentException.class)
        public void testException2(){
            Date date = DateUtil.toDate("2018-11-18 12:00:12");
            CronUtil.calculate("1-62 15 10 ? * *", date);
        }
        @Test(expected = IllegalArgumentException.class)
        public void testException3(){
            Date date = DateUtil.toDate("2018-11-18 12:00:12");
            CronUtil.calculate("1 2-78 10 ? * *", date);
        }
        @Test(expected = IllegalArgumentException.class)
        public void testException4(){
            Date date = DateUtil.toDate("2018-11-18 12:00:12");
            CronUtil.calculate("2 15 25 ? * *", date);
        }
        @Test(expected = IllegalArgumentException.class)
        public void testException5(){
            Date date = DateUtil.toDate("2018-11-18 12:00:12");
            CronUtil.calculate("2 15 23 2-32 * *", date);
        }
        @Test(expected = IllegalArgumentException.class)
        public void testException6(){
            Date date = DateUtil.toDate("2018-11-18 12:00:12");
            CronUtil.calculate("2 15 23 3 1-13/2 *", date);
        }
    }
    

     

    展开全文
  • 计划任务,是任务在约定的时间执行已经计划好的工作,这是表面的意思
  • Cron表达式学习:每天十二点执行一次:0 0 12 * * ?

    万次阅读 多人点赞 2020-04-17 15:38:38
    1.cron表达字符含义 语法: Seconds Minutes Hours DayofMonth Month DayofWeek Year Seconds Minutes Hours DayofMonth Month DayofWeek 比较多的使用的通常是第二条,因为很少任务是按年份去执行的 2.常用表达式...

    首先推荐大家一个CRON表达式在线生成网站

    http://cron.qqe2.com/

    1.cron表达字符含义

    cron表达式:* * * * * *,其实就是一个字符串,
    字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,其中常用为六个域。
    corn从左到右(用空格隔开)分别是:秒 分 小时 月份中的日期 月份 星期中的日期 年份

    字段允许值允许的特殊字符
    秒(Seconds)0~59的整数, - * / 四个字符
    分(Minutes)0~59的整数, - * / 四个字符
    小时(Hours)0~23的整数, - * / 四个字符
    日期(DayofMonth)1~31的整数(但是你需要考虑你月的天数),- * ? / L W C 八个字符
    月份(Month)1~12的整数或者 JAN-DEC, - * / 四个字符
    星期(DayofWeek)1~7的整数或者 SUN-SAT (1=SUN), - * ? / L C # 八个字符
    年(可选,不常用)(Year)1970~2099, - * / 四个字符

    2.corn各个字段的含义

    (1):表示匹配该域的任意值。假如在Minutes域使用, 即表示每分钟都会触发事件。

    (2)?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发,实际上并不是这样。

    (3)-:表示范围。例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次

    (4)/:表示起始时间开始触发,然后每隔固定时间触发一次。例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次.

    (5),:表示列出枚举值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。

    (6)L:表示最后,只能出现在DayofWeek和DayofMonth域。如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。

    (7)W:表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。例如:在 DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日(周一)触发;如果5日在星期一到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份 。

    (8)LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。

    (9)#:用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。

    3.常用表达式示范

    • 每隔5秒执行一次:*/5 * * * * ?
    • 每隔1分钟执行一次:0 */1 * * * ?
    • 每小时的20分执行一次:0 20 * * * ?
    • 每天的两点35分执行一次:0 35 2 * * ?
    • 每月的1日的凌晨2点调整任务:0 0 2 1 * ? *
    • 每天上午10点,下午2点,4点 执行一次:0 0 10,14,16 * * ?
    • 每周星期天凌晨1点实行一次 :0 0 1 ? * L

    4.cron表达式在springboot工程中的应用

    在springboot的主类上,加上注解@EnableScheduling

    @SpringBootApplication
    @EnableScheduling
    public class MybatisplusdemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MybatisplusdemoApplication.class, args);
        }
    
    }
    

    编写测试类

    package com.wyu.mybatisplusdemo.article.controller;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import org.springframework.stereotype.Component;
    import org.springframework.scheduling.annotation.Scheduled;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    
    @Component
    public class Schedule {
        Logger log = LoggerFactory.getLogger(Schedule.class);
    
        //cron表达式:每隔5秒执行一次
        @Scheduled(cron = "0/5 * * * * *")
        public void scheduled(){
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("hh点mm分ss秒");
            // 将date日期解析为字符串
            String date = simpleDateFormat.format(new Date());
            log.info("当前时间:" + date);
        }
    
    }
    
    

    运行结果
    在这里插入图片描述

    5. 手写cron生成工具类

    @ApiModel(description = "cron表达体")
    @Data
    @Accessors(chain = true)
    @EqualsAndHashCode()
    public class CronEntity extends Model<CronEntity> {
        /**
         * 周期: DAY:每天,WEEK:每周,MONTH:每月
         */
        String cycle;
    
        /** 一周的哪几天 */
        String[] dayOfWeeks;
    
        /** 一个月的哪几天 */
        String[] dayOfMonths;
    
        /** 频次,一天执行多少次 */
        Integer frequency;
    
        /** 时 */
        Integer hour;
    
        /** 分 */
        Integer minute;
    
    }
    
    /**
     * 构建日周月的Cron表达式
     */
    @Slf4j
    public class CronUtil {
    
        public static String createCronExp(CronEntity cronEntity){
            StringBuffer cronExp = new StringBuffer("");
            if (cronEntity.getFrequency() == null){
                cronEntity.setFrequency(1);
            }
            if (null != cronEntity.getMinute() && null != cronEntity.getHour()) {
                cronExp.append(0).append(" ");
                cronExp.append(cronEntity.getMinute()).append(" ");
                cronExp.append(cronEntity.getHour()).append(" ");
                if(cronEntity.getCycle().equals("DAY")){
                    cronExp.append("*/").append(cronEntity.getFrequency()).append(" ");
                    cronExp.append("* ");
                    cronExp.append("?");
                }
                else if(cronEntity.getCycle().equals("WEEK")){
                    cronExp.append("? ");
                    cronExp.append("* ");
                    String[] weeks = cronEntity.getDayOfWeeks();
                    for(int i = 0; i < weeks.length; i++){
                        if(i == 0){
                            cronExp.append(weeks[i]);
                        } else{
                            cronExp.append(",").append(weeks[i]);
                        }
                    }
                }
                else if(cronEntity.getCycle().equals("MONTH")){
                    String[] days = cronEntity.getDayOfMonths();
                    for(int i = 0; i < days.length; i++){
                        if(i == 0){
                            cronExp.append(days[i]);
                        } else{
                            cronExp.append(",").append(days[i]);
                        }
                    }
                    cronExp.append(" * ");
                    cronExp.append("?");
                }
            }else {
                cronExp.append("Missing time parameter");
                log.info("分钟小时参数未设置");
            }
            return cronExp.toString();
        }
    
        //测试
        public static void main(String[] args) {
            CronEntity cronEntity = new CronEntity();
            cronEntity.setFrequency(1);
            cronEntity.setCycle("WEEK");
            String[] str = {"2","3"};
            cronEntity.setDayOfWeeks(str);
            cronEntity.setMinute(20);
            cronEntity.setHour(12);
            String cronExp = createCronExp(cronEntity);
            log.info(cronExp);
        }
    }
    

    参考文章

    cron表达式详解

    展开全文
  • #UpdateOrderStatusJob.cron=*/1 * * * * #每一分钟执行一次 UpdateOrderStatusJob.enable=true ``` Exception in thread "main" java.lang.RuntimeException: it.sauronsoftware.cron4j.InvalidPatternException:...
  • Cron范式每日凌晨一点执行

    万次阅读 2020-08-10 11:23:19
    最近开发中遇到需要定时执行的任务,使用xxl-job创建任务,创建任务,定时执行,在描述任务时,使用cron表达式来描述任务执行间隔,常用比如:每天凌晨一点执行,每月最后一天执行等,下面记录一些常用的表达式。...

    最近开发中遇到需要定时执行的任务,使用xxl-job创建任务,创建任务,定时执行,在描述任务时,使用cron表达式来描述任务执行间隔,常用比如:每天凌晨一点执行,每月最后一天执行等,下面记录一些常用的表达式。

    表达式:

    秒 分 时 日 月 周 年(可选)。

               字段名                 允许的值                        允许的特殊字符  
               秒                         0-59                               , - * /  
               分                         0-59                               , - * /  
               小时                     0-23                               , - * /  
               日                         1-31                               , - * ? / L W C  
               月                         1-12 or JAN-DEC         , - * /  
               周几                     1-7 or SUN-SAT           , - * ? / L C #  
               年 (可选字段)     empty, 1970-2099      , - * /
    
    
    
               “?”字符:表示不确定的值
    
               “,”字符:指定数个值
    
               “-”字符:指定一个值的范围
    
               “/”字符:指定一个值的增加幅度。n/m表示从n开始,每次增加m
    
               “L”字符:用在日表示一个月中的最后一天,用在周表示该月最后一个星期X
    
               “W”字符:指定离给定日期最近的工作日(周一到周五)
    
               “#”字符:表示该月第几个周X。6#3表示该月第3个周五
    

    范例:

                 每隔5秒执行一次:*/5 * * * * ?
    
                 每隔1分钟执行一次:0 */1 * * * ?
    
                 每天23点执行一次:0 0 23 * * ?
    
                 每天凌晨1点执行一次:0 0 1 * * ?
    
                 每月1号凌晨1点执行一次:0 0 1 1 * ?
    
                 每月最后一天23点执行一次:0 0 23 L * ?
    
                 每周星期天凌晨1点实行一次:0 0 1 ? * L
    
                 在26分、29分、33分执行一次:0 26,29,33 * * * ?
    
                 每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?
    
    展开全文
  • 捏麻麻的 java quartz cron表达式 这坑坑了我一下午 居然没人遇到过 指定2021年7月18号以后每天都运行一遍直到自己手动关闭 0 0 0 18/1 7/1 ? 2021/1 证明每个月都能运行 只需要把日期调成二十天: cron表达式...
  • cron表达式:按周执行时需留意

    千次阅读 2020-04-03 15:49:49
    前几天使用cron表达式做任务的自动调度,使用按周执行的时候,遇到一个问题。 比如cron = 0 15 10 ? * 1 2020 ,我想要的是 2020年每周一的10:15执行,结果实际每次都是周二的1015才执行,折腾半天发现,外国对于...
  • 一、Cron表达式是一个字符串,字符串以空格隔开,分为5或6个域,每一个域代表一个含义,系统支持的表达式格式如下(DayofWeek 为可选域。): Seconds Minutes Hours DayofMonth Month [DayofWeek] 1 二、字段含义...
  • 0 0/1 8-18 ? * 2-6 转载于:https://www.cnblogs.com/yshyee/p/6867108.html
  • 常用表达式例子    0/2 * * * * ?... * MON-FRI 表示周一到周五每天上午10:15执行作业 0 15 10 ? 6L 2002-2006 表示2002-2006年的每个月的最后一个星期五上午10:15执行作 0 0 10,14,16 * *...
  • cron表达式详解

    2012-05-09 16:42:47
    cron,cron表达式,cron表达式详解
  • cron表达式详解,cron表达式写法,cron表达式例子

    万次阅读 多人点赞 2018-01-03 12:55:09
    先了解每个位置代表的含义,在了解每个位置允许的范围,以及一些特殊写法,还有常用的案例,足够你掌握cron表达式
  • 常用的cron表达式

    千次阅读 2018-07-06 14:13:00
    CRON表达式 含义"*/5 * * * * ? " 每隔5秒执行一次"0 */1 * * * ? " 每隔1分钟执行一次"0 0 12 * * ?" 每天中午十二触发"0 0 23 * * ?" 每天23点执行一次"0 0 ...
  • Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式: Seconds Minutes Hours DayofMonth Month DayofWeek Year或 Seconds Minutes Hours DayofMonth ...
  • cron表达式

    千次阅读 2019-09-27 18:18:07
    "表示从每天的凌晨1到下午1之间(包括凌晨1和下午1)每个小时都要执行一次指定任务 "*" 表示所在域的任意值 例如:"0 * 1 * * ?"表示每天的凌晨1到两之间的每分钟都要执行一次指定任务 /:表示从起始...
  • java定时执行任务CRON表达式

    万次阅读 2018-11-24 17:21:05
    CRON表达式在线验证网址: http://cron.qqe2.com/ 在类上使用@Scheduled注解例子: ... @Scheduled(cron="0/5 * * * * ? ") //定时执行任务注解 public class Quartz...## 以下为CRON表达式规则正文: ...
  • Quartz Cron表达式(定时任务)

    千次阅读 2018-05-11 10:45:13
    参考链接 ...  http://www.blogjava.net/fancydeepin/archive/2012/06/12/quartz-cron.html   表达式的使用格式: 表达式按照顺序分别为秒,分,时,日,月,周,年(其中只有年是可选字...
  • 系统中通常有些需要自动执行的任务,这些任务可能每隔一段时间就要执行一次,也可能需要在指定的时间自动执行,这些任务的自定执行必须使用任务的自动调度。 JDK为简单的任务调度提供了Timer支持,单对于更复杂...
  • Quartz cron表达式(时间表达式)

    万次阅读 2017-03-28 09:56:42
    Cron表达式示例:表达式 说明 "0 0 12 * * ? " 每天12运行 "0 15 10 * * ?" 每天10:15运行 "0 15 10 * * ? 2011" 2011年的每天10:15运行 "0 * 14 * * ?" 每天14到15之间每分钟运行一次,开始于14:00,结束于...
  • Cron表达式范例: 每隔5秒执行一次:*/5 * * * * ? 每隔1分钟执行一次:0 */1 * * * ? 每天23点执行一次:0 0 23 * * ? 每天凌晨1点执行一次:0 0 1 * * ? 每月1号凌晨1点执行一次:0 0 1 1 * ? 每月最后...
  • /** * @Classname CronUtil * @Description TODO * @Date 2019/7/30 17:39 * @Created by zhangzhenjun */ public class CronUtil { /** * *方法摘要:构建Cron表达式 *@param taskScheduleModel *@return String *...
  • } } } 后端执行逻辑 Trigger 字段 执行日期从 2021-09-19 开始,2021-09-25 结束,每天0903分执行 { "startDate":"2021-09-19", "endDate":"2021-09-25", "cron":"0 3 9 * * ?" } private static final String...
  • QuartZ Cron表达式 貌似指定不了 某一天这一辈子执行1次那种
  • Cron表达式范例: 每隔5秒执行一次:*/5 * * * * ? 每隔1分钟执行一次:0 */1 * * * ? 每天23点执行一次:0 0 23 * * ? 每天凌晨1点执行一次:0 0 1 * * ? 每月1号凌晨1点执行一次:0 0 1 1 * ? 每月最后...
  • JAVA根据每天每周每月生成cron表达式

    千次阅读 2020-06-30 16:18:40
    注意:按每周执行时,选择的周一 需要传2,周二-3,周三-4,周四-5,周五-6,周六-7,周日-1,按照该对应关系才可。 对应的转换cron工具类如下 package com.develop.common.utils; import java.text....
  • 常用Cron表达式

    千次阅读 2019-04-02 10:57:34
    Cron语法格式,共7个部分(域)组成: Seconds(秒) Minutes(分钟) Hours(小时) DayofMonth(天/几号) Month(月) DayofWeek(星期几) Year(年) 每一个域可出现的字符如下: Seconds:可出现", - * /",有效范围为0-59的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 13,103
精华内容 5,241
关键字:

cron表达式每天8点执行