• cron表达式增加一段时间变为新的表达式


    cron表达式是使用任务调度经常使用的表达式了。对于通常的简单任务,我们只需要一条cron表达式就能满足。但是有的时候任务也可以很复杂。
    最近我遇到了一个问题,一条任务在开始的时候要触发A方法,在结束的时候需要触发B方法。所以每次我添加触发器的时候都需要两个cron表达式,两个表达式需要间隔一定的时间。听起来特别复杂,但是实际上我只需要实现每天、每周、每月的时间就可以了。
    选择每天时,持续时间不超过一天。
    选择每周时,持续时间不超过一周。
    选择每月时,持续时间不超过30天。

    public class cronExpressionAddDuration {
    	public static void main(String[] args) {
    		/* {"cron":"0 0 0 31 * ?","duration":2678400}*/
    		/* {"cron":"0 20 0 ? * 7","duration":604740}*/
    		/* {"cron":"0 20 0 * * ?","duration":86340}*/
    		/* {"cron":"0 59 23 L * ?","duration":2678340} */
    		String cron="cron":"0 59 23 L * ?";
    		int duration=2678340;
    		String newCron=cronExpressionPlusDuration(cron,duration);
    		System.out.println(newCron);
    	}
    	  /**
         * <p>对cron表达式进行修改,根据传入的时间生成一个新的cron表达式</>
         * 两个表达式之间的间隔为duration
         * 已知缺陷:
         * 1.如果期望添加一个暂停方式是每月且持续超过28天的表达式,那么在二月份该表达式不会生效。此方法也是cron表达式的缺陷
         * 2.如果期望添加一个暂停方式是每月的表达式,例如每月最后一天00:00持续时间5天。
         *   假如我在3号添加了这条规则,如果此时该任务还有一个规则是5号不应该恢复,理论上我们不希望再次触发。但实际上此时这条规则会在5号触发。
         *   不过虽然{@code cron}表达式的触发不能满足要求,但是在恢复采集的方法中已经增加了任务是否可以恢复的方法
         *   因此即使错误时间触发,但实际执行时并不会恢复采集。
         *
         * @param cron
         * @param duration 单位为秒
         * @return
         */
        private static String cronExpressionPlusDuration(String cron, int duration) {
           
            String[] cronArray = cron.split(" ");
            int days = duration / 86400;
            int hours = (duration % (86400)) / 3600;
            int minutes = ((duration % (86400)) % 3600) / 60;
            cronArray[2] = String.valueOf(Integer.valueOf(cronArray[2]) + hours);
            cronArray[1] = String.valueOf(Integer.valueOf(cronArray[1]) + minutes);
            /* 对小时和分钟进行合法性校验*/
            if (Integer.valueOf(cronArray[1]) >= 60 || Integer.valueOf(cronArray[2]) >= 24) {
                int extraMinutes = Integer.valueOf(cronArray[1]) % 60;
                int extraHours = (Integer.valueOf(cronArray[2])+ Integer.valueOf(cronArray[1]) / 60)%24;
                cronArray[2] = String.valueOf(extraHours);
                cronArray[1] = String.valueOf(extraMinutes);
            }
            /* 持续时间一天以内*/
            if (duration < 86400 && "*".equals(cronArray[3]) && "*".equals(cronArray[4]) && "?".equals(cronArray[5])) {
    
            } else if (duration > 86400 && duration < 86400 * 7) {
                cronArray[5] = String.valueOf((Integer.valueOf(cronArray[5]) + days) % 7 + 1);
            } else if (duration > 86400 * 7 && duration < 86400 * 32) {
                if("L".equals(cronArray[3])){
                    cronArray[3]=String.valueOf(days);
                }else {
                    cronArray[3] = String.valueOf(((Integer.valueOf(cronArray[3]) + days) % 31) + 1);
                }
            }
            String result = "";
            for (String s : cronArray) {
                result += s + " ";
            }
            return result;
        }
    }
    

    但是这个方法有两个个缺陷。

    1. 如果期望添加一个暂停方式是每月且持续时间超过28天的表达式,那么在二月份该表达式不会生效。这个问题暂时没法解决
    2. 如果期望添加一个暂停方式是每月的表达式,例如每月最后一天00:00持续时间5天。 假如我在3号添加了这条规则,如果此时该任务还有一个规则是5号不应该恢复,理论上我们不希望再次触发。但实际上此时这条规则会在5号触发。也就是说在我们真正的任务还没触发前,新的任务已经触发了。这样肯定会有一定的问题。

    第二个缺陷我是这样修复的。在实际触发的job里面增加一条规则的判断,触发任务的时候先判断当前时间是否在我们预期规则的时间内,如果在就触发,否则就不触发。

    boolean isPausedCurrently(String rule,long duration) {
             
                CronExpression cronExpression;
                try {
                    cronExpression = new CronExpression(cron);
                } catch (ParseException e) {
                    log.error("解析表cron达式错误:" + expr, e);
                    return false;
                }
                /* 获取当前时间(毫秒)*/
                long now = Instant.now().toEpochMilli();
                long time = now - duration * 1000;
                Date date = cronExpression.getNextValidTimeAfter(new Date(time));
                if (date.getTime() < now) {
                    return true;
                }
            }
            return false;
        }
    

    这样判断之后就可以解决上面的缺陷了。

  • 相关阅读:
    ruby直接底层连接数据库
    debian和ubuntu的sh dash bash
    find locate
    apt-get
    ERROR: The partition with /var/lib/mysql is too full! failed!
    linux访问ftp服务器命令
    win7配置ftp服务器
    黑马程序员_Java基础视频-深入浅出精华版--PPT 文件列表
    黑马程序员_Java基础视频-深入浅出精华版--视频列表
    转:Java项目开发规范参考
  • 原文地址:https://www.cnblogs.com/rever/p/10190426.html
Copyright © 2020-2023  润新知