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 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;
}
/**
* 设置定时任务的,时间段范围,可以有多个
*/
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;
}
}
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);
}
}