package com.walker.scheduler; import com.walker.infrastructure.utils.DateUtils; import com.walker.infrastructure.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * 调度器时间设置选项定义 * @author shikeying * @date 2015年12月24日 * */ public class Option { protected transient final Logger logger = LoggerFactory.getLogger(this.getClass()); private static final DateFormat whippletreeTimeFormat = new SimpleDateFormat("yyyy MM dd HH mm"); private PeriodType periodType = PeriodType.NONE; private TimeType timeType = TimeType.EXACTLY; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // 以下属性是'精确时间点'设置 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private int hour = 0; private int day = 1; private int month = 1; private int year = 2015; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // 以下属性是'时间段'设置 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private List timeRanges = new ArrayList(2); // 内部计数器,在周期性调度中使用 private boolean isCycleTask = true; // 是否周期性调度器,运行一次也算周期性 /** * 是否周期性任务,如果是返回true * @return */ public boolean isCycleTask() { return isCycleTask; } // private int currentTaskHour = 0; // 周期性,当前执行到的小时,记录 private int currentTaskDay = 0; // 周期性,当前执行到的天,记录 private int currentTaskMonth = 0; // 周期性,当前执行到的月,记录 private int currentTaskYear = 0; // 周期性,当前执行到的年,记录 public int getCurrentTaskDay() { return currentTaskDay; } public int getCurrentTaskMonth() { return currentTaskMonth; } public int getCurrentTaskYear() { return currentTaskYear; } public String getTimeRangesValue() { StringBuilder s = new StringBuilder(); for(Integer[] vals : timeRanges){ s.append(vals[0]); s.append(","); s.append(vals[1]); } return s.toString(); } public Option(){} public TimeType getTimeType() { return timeType; } public PeriodType getPeriodType() { return periodType; } public void setTimeType(TimeType timeType) { this.timeType = timeType; } public void setPeriodType(PeriodType periodType) { this.periodType = periodType; if(periodType == PeriodType.DAY || periodType == PeriodType.WEEK || periodType == PeriodType.MONTH || periodType == PeriodType.YEAR || periodType == PeriodType.NONE){ this.isCycleTask = true; } else { this.isCycleTask = false; } } /** * 设置定时任务的精确时间 * @param year * @param month * @param day * @param hour */ public void setExactlyTime(int year, int month, int day, int hour){ this.year = year; this.month = month; this.day = day; this.hour = hour; this.currentTaskYear = year; this.currentTaskMonth = month; this.currentTaskDay = day; // this.currentTaskHour = hour; // 2024-01-18,修复:如果已经过了今天时间点的任务,如:凌晨2点,则需要自动切换到下一个时间。 // 否则出现任务永远无法执行情况。 if(this.isCycleTask){ Option.TimeObject timeObj = this.isAvailableTime(System.currentTimeMillis()); int currentHour = DateUtils.getCurrentHour(); int currentDay = DateUtils.getCurrentYearMonthDay()[2]; if(this.periodType == PeriodType.DAY){ if(currentHour > hour){ // 今天已经过了时间点,自动切换到下一次 this.scheduleToNext(timeObj); logger.info("今天已经过了时间点,自动切换到下一次,option={}", this); } } else if (this.periodType == PeriodType.MONTH) { if(currentDay > day){ this.scheduleToNext(timeObj); logger.info("本月当前日期{}已超过设置日期{},自动切换到下一次,option={}", currentDay, day, this); } else if(currentDay == day && (currentHour > hour)){ this.scheduleToNext(timeObj); logger.info("本月日期已到达,但由于时间已过期,因此任务会在下个月执行,option={}", this); } } else if (this.periodType == PeriodType.YEAR) { int currentMonth = DateUtils.getCurrentYearMonthDay()[1]; if(currentMonth > month){ this.scheduleToNext(timeObj); logger.info("本年当前月份{}已超过设置月份{},自动切换到下一次,option={}", currentMonth, month, this); } else if (currentMonth == month && (currentDay > day)) { this.scheduleToNext(timeObj); logger.info("本年当前月份相同,但当前日期{}已超过设置日期{},自动切换到下一次,option={}", currentDay, day, this); } else if(currentMonth == month && (currentDay == day) && currentHour > hour){ this.scheduleToNext(timeObj); logger.info("本年当前月份日期相同,但当前时间{}已超过设置时间{},自动切换到下一次,option={}", currentHour, hour, this); } } } } /** * 设置定时任务的,时间段范围,可以有多个 */ public void setRangeTime(List timeRanges){ if(timeRanges == null || timeRanges.size() == 0){ throw new IllegalArgumentException("设置的时间范围数据必须存在"); } for(Integer[] r : timeRanges){ if(r.length != 2){ throw new IllegalArgumentException("设置时间范围集合中,每个数组表示一个范围:开始钟点、结束钟点。如:12~13"); } } this.timeRanges = timeRanges; } /** * 系统给定的时间,是否满足(调度任务)设定时间的要求

* 该方法返回的是对象TimeObject,里面有状态信息。
* timeObject.isAvailable();返回了是否有效时间。 * @param currentTime * @return */ public TimeObject isAvailableTime(long currentTime){ return this.doCheckAvailable(currentTime); } /** * 系统给定的时间,是否满足(调度任务)设定时间的要求 * @param currentTime * @return */ public boolean isAvailable(long currentTime){ return doCheckAvailable(currentTime).isAvailable(); } /** * 是周期性调度时,调用该方法切换到下一个时间点。 * @param currentTimeObj */ public void scheduleToNext(TimeObject currentTimeObj){ if(this.isCycleTask){ if(this.periodType == PeriodType.DAY || this.periodType == PeriodType.NONE){ int[] nextDateInfo = DateUtils.getNextDay(currentTimeObj.getYear(), currentTimeObj.getMonth(), currentTimeObj.getDay(), 1); this.currentTaskYear = nextDateInfo[0]; this.currentTaskMonth = nextDateInfo[1]; this.currentTaskDay = nextDateInfo[2]; } else if(this.periodType == PeriodType.MONTH){ // 每个月的:几号、几点执行 this.currentTaskYear = currentTimeObj.year; if(this.currentTaskMonth < 12){ this.currentTaskMonth++; } else { this.currentTaskMonth = 1; } } else if(this.periodType == PeriodType.YEAR){ this.currentTaskYear++; } else { throw new UnsupportedOperationException(); } } } private TimeObject doCheckAvailable(long currentTime){ TimeObject timeObj = getTimeInfo(currentTime); boolean result = false; if(this.isCycleTask){ // 周期类型调度,必须精确到年月日时 if(this.periodType == PeriodType.DAY){ if(this.currentTaskDay == timeObj.day && this.hour == timeObj.hour){ result = true; } } else if(this.periodType == PeriodType.WEEK){ throw new UnsupportedOperationException("还未实现按周调度"); } else if(this.periodType == PeriodType.MONTH){ if(this.currentTaskMonth == timeObj.month && this.day == timeObj.day && this.hour == timeObj.hour){ result = true; } } else if(this.periodType == PeriodType.YEAR){ if(this.currentTaskYear == timeObj.year && this.month == timeObj.month && this.day == timeObj.day && this.hour == timeObj.hour){ result = true; } } else if(this.periodType == PeriodType.NONE){ if(this.year == timeObj.year && this.month == timeObj.month && this.day == timeObj.day && this.hour == timeObj.hour){ result = true; } } else { throw new UnsupportedOperationException(); } } else { // 采集类型调度 result = this.doAnalizeHourMatch(timeObj); } timeObj.setAvailable(result); return timeObj; /** if(this.periodType == PeriodType.DAY){ // 直接比较时间 result = this.doAnalizeHourMatch(timeObj); } else if(this.periodType == PeriodType.WEEK){ throw new UnsupportedOperationException("还未实现按周调度"); } else if(this.periodType == PeriodType.MONTH){ // 比较日期是否对应 if(timeObj.isSameDay(day)){ result = this.doAnalizeHourMatch(timeObj); } } else if(this.periodType == PeriodType.YEAR){ // 比较月份、日期是否对应 if(timeObj.isSameDay(month, day)){ result = this.doAnalizeHourMatch(timeObj); } } else { // 不重复,仅执行一次,年月日都比较 // if(timeObj.isSameDay(year, month, day)){ // result = this.doAnalizeHourMatch(timeObj); // } //改为立即执行 result = true; } timeObj.setAvailable(result); return timeObj; **/ } private boolean doAnalizeHourMatch(TimeObject timeObj){ if(this.timeType == TimeType.EXACTLY){ if(timeObj.getHour() >= this.hour){ logger.debug("超过设定时间,执行任务"); return true; } } else { // 时间段判断 for(Integer[] r : timeRanges){ if(timeObj.getHour() >= r[0] && timeObj.getHour() <= r[1]){ logger.debug("------匹配了时间段:" + r[0]); return true; } } } return false; } private TimeObject getTimeInfo(long currentTime){ String showDate = whippletreeTimeFormat.format(new Date(currentTime)); // String[] showDateArray = showDate.split(" "); String[] showDateArray = showDate.split(StringUtils.CHAR_SPACE); TimeObject timeObj = new TimeObject(Integer.parseInt(showDateArray[0]) , Integer.parseInt(showDateArray[1]) , Integer.parseInt(showDateArray[2]) , Integer.parseInt(showDateArray[3])); // logger.debug(timeObj); return timeObj; } /** * 执行周期定义 * @author shikeying * @date 2015年12月23日 * */ public enum PeriodType { NONE{ public String getIndex(){ return PERIOD_TYPE_ONCE; } } , DAY{ public String getIndex(){ return PERIOD_TYPE_CYCLE_DAY; } } , WEEK{ public String getIndex(){ return PERIOD_TYPE_CYCLE_WEEK; } } , MONTH{ public String getIndex(){ return PERIOD_TYPE_CYCLE_MONTH; } } , YEAR{ public String getIndex(){ return PERIOD_TYPE_CYCLE_YEAR; } }, FOREVER { public String getIndex(){ return PERIOD_TYPE_FOREVER; } }; public String getIndex(){ throw new AbstractMethodError(); } public static PeriodType getObject(String index){ if(index.equals(PERIOD_TYPE_ONCE)){ return NONE; } else if(index.equals(PERIOD_TYPE_CYCLE_DAY)){ return DAY; } else if(index.equals(PERIOD_TYPE_CYCLE_WEEK)){ return WEEK; } else if(index.equals(PERIOD_TYPE_CYCLE_MONTH)){ return MONTH; } else if(index.equals(PERIOD_TYPE_CYCLE_YEAR)){ return YEAR; } else if(index.equals(PERIOD_TYPE_FOREVER)){ return FOREVER; } else { throw new IllegalArgumentException(); } } public static final String PERIOD_TYPE_ONCE = "none"; // 只执行一次,即:任务已发布就执行 public static final String PERIOD_TYPE_CYCLE_DAY = "day"; // 周期性:每天某个时间(或范围) public static final String PERIOD_TYPE_CYCLE_WEEK = "week"; // 周期性:每周某个时间(或范围) public static final String PERIOD_TYPE_CYCLE_MONTH = "month";// 周期性:每月某个时间(或范围) public static final String PERIOD_TYPE_CYCLE_YEAR = "year"; // 周期性:每年某个时间(或范围) public static final String PERIOD_TYPE_FOREVER = "forever"; // 采集类型:永远循环执行,需要设置间隔时间等参数 } /** * 执行时间类型定义 * @author shikeying * @date 2015年12月23日 * */ public enum TimeType { /** * 精确时间点 */ EXACTLY{ public String getIndex(){ return "exactly"; } } /** * 时间段范围 */ , RANGE{ public String getIndex(){ return "ranges"; } }; public String getIndex(){ throw new AbstractMethodError(); } public static TimeType getObject(String index){ if(index.equals("exactly")){ return EXACTLY; } else { return RANGE; } } } public class TimeObject{ private int year = 2015; private int month = 1; private int day = 1; private int hour = 1; // 标识,当前的时间对象是否有效的调度时间 // 在执行循环时,增加该属性能判断是否切换了日期 // 2016-01-26 时克英 private boolean available = false; /** * 标识,当前的时间对象是否有效的调度时间 * @return */ public boolean isAvailable() { return available; } public void setAvailable(boolean available) { this.available = available; } public int getYear() { return year; } public int getMonth() { return month; } public int getDay() { return day; } public int getHour() { return hour; } public TimeObject(int year, int month, int day, int hour){ this.year = year; this.month = month; this.day = day; this.hour = hour; } @Override public String toString(){ return new StringBuilder().append("[year=").append(year) .append(", month=").append(month) .append(", day=").append(day) .append(", hour=").append(hour) .append("]").toString(); } public boolean isSameDay(int year, int month, int day){ if(this.year == year && this.month == month && this.day == day){ return true; } return false; } public boolean isSameDay(int month, int day){ if(this.month == month && this.day == day){ return true; } return false; } public boolean isSameDay(int day){ if(this.day == day){ return true; } return false; } } @Override public String toString(){ return new StringBuilder("[periodType=").append(this.periodType) .append(", timeType=").append(this.timeType) .append(", timeRanges=").append(this.timeRanges) .append(", isCycleTask=").append(this.isCycleTask) .append(", setYearMonthDayHour=").append(this.year).append("-").append(this.month).append("-").append(this.day).append("-").append(this.hour) .append(", nextTime=").append(this.currentTaskYear).append("-").append(this.currentTaskMonth).append("-").append(this.currentTaskDay) .append("]").toString(); } public static void main(String[] args){ /** long test = System.currentTimeMillis(); Option option = new Option(); // option.setExactlyTime(2015, 12, 23, 17); List timeRanges = new ArrayList(2); timeRanges.add(new Integer[]{9,10}); timeRanges.add(new Integer[]{12,13}); option.setTimeType(TimeType.RANGE); option.setRangeTime(timeRanges); // 设置每天执行 // option.setPeriodType(PeriodType.DAY); // option.setExactlyTime(0, 0, 0, 0); // 设置每月执行 // option.setPeriodType(PeriodType.MONTH); // option.setExactlyTime(0, 0, 24, 0); // 设置每年执行 option.setPeriodType(PeriodType.YEAR); option.setExactlyTime(0, 12, 24, 0); option.isAvailable(test); **/ testCycleDay(); testCycleMonth(); } private static void testCycleDay(){ Option option = new Option(); option.setPeriodType(PeriodType.DAY); option.setTimeType(Option.TimeType.EXACTLY); option.setExactlyTime(2023, 12, 31, 11); TimeObject timeObj = option.isAvailableTime(System.currentTimeMillis()); System.out.println("第一次调用结果:" + timeObj.isAvailable()); option.scheduleToNext(timeObj); System.out.println("切换后,调用结果:" + option.isAvailable(System.currentTimeMillis())); System.out.println(option.currentTaskYear + "年" + option.currentTaskMonth + "月" + option.currentTaskDay + "日"); } private static void testCycleMonth(){ Option option = new Option(); option.setPeriodType(PeriodType.MONTH); option.setExactlyTime(2019, 1, 2, 16); TimeObject timeObj = option.isAvailableTime(System.currentTimeMillis()); System.out.println("第一次调用结果:" + timeObj.isAvailable()); option.scheduleToNext(timeObj); System.out.println("切换后,调用结果:" + option.isAvailable(System.currentTimeMillis())); System.out.println(option.currentTaskMonth); } }