java自定義排程定時器工具類(java電商訂單自動失效或收貨)
阿新 • • 發佈:2018-12-06
java電商訂單超時改狀態工具類
最近在做一個電商專案,要求在使用者下單後未付款30分鐘後就將訂單的狀態改為失效,最初想的是用定時器沒幾秒去資料庫檢視有哪些訂單未付款但超過30分的,就修改狀態,這個方式有兩種缺點,一:如果時間設定的較短,就會導致一直在讀寫資料庫,二:如果時間設定較長就會導致時間不精確,所以就想到自己寫一個工具類。訂單失效思想:當第一次有人下單時,啟動定時器,延長半個小時後的時間作為定時器Timer的安排時間,當下第二個訂單時檢測,(第一個 訂單的失效時間始終要比第二個訂單失效時間少)是否建立了這個定時器,如果建立個就不管了,如果沒有建立就建立,當在執行定時器任務,先判斷這個訂單是不是還是未付款狀態,如果是就將狀態改為失效,接著檢測所有未付款訂單裡最先下單的時間讀出 來加上30鍾作為下一次定時器的執行時間,這樣做就可以減輕伺服器和資料庫的壓力。
如上面所說只用寫一個簡單的類就能完成了,在寫的時候我覺得把他寫成一個工具類,方便以後用,下面是程式碼:
首先想想到的是時間型別:時間有毫秒值間隔,時間類Date,還有cron表示式
前兩種比較簡單,而cron表示式比較麻煩,所以就簡單的寫了一下cron表示式解析(不完全,算一個最基本的解析):
簡易cron表示式解析:
/** * 解析corn表示式,將表示式所有的(秒,分,時,日,月,星期,年)可能性存在Map裡 * 表示式可以為:x x-y x/y [x,x1,x2] * @param cron * @return */ public static Map<String, List<Integer>> parsingCron(String cron) { String[] strings = cron.split(" "); List<String> stringList = new ArrayList<>(); for (String s : strings) { stringList.add(s); } if (stringList.contains("")) { System.out.println("stringList:" + stringList); System.out.println("表達是中有多餘的空值,即將清除"); while (stringList.remove("")) ; System.out.println("已移除多餘的空值"); } if (stringList.size() > 7) { throw new RuntimeException("表達是不正確:超長"); } //過短的表示式,補*號 while (stringList.size() != 7) { stringList.add("*"); } System.out.println("stringList:" + stringList); Map<String, List<Integer>> map = new HashMap<>(); for (int i = 0; i < 7; i++) { switch (i) { case 0: map.put("秒", readOneCronValue("秒", stringList.get(0), 59, 0)); break; case 1: map.put("分", readOneCronValue("分", stringList.get(1), 59, 0)); break; case 2: map.put("時", readOneCronValue("時", stringList.get(2), 23, 0)); break; case 3: map.put("日", readOneCronValue("日", stringList.get(3), 31, 1)); break; case 4: map.put("月", readOneCronValue("月", stringList.get(4), 12, 1)); break; case 5: map.put("星期", readOneCronValue("星期", stringList.get(5), 7, 1)); break; case 6: map.put("年", readOneCronValue("年", stringList.get(6), 2099, 1970)); break; } } System.out.println(map); return map; } /** * 表示式可能性值處理方法 * 如果表示式裡有字母會拋轉換異常 * @param name * @param str * @param max * @param min * @return */ public static List<Integer> readOneCronValue(String name, String str, Integer max, Integer min) { System.out.println(name + " :" + str); List<Integer> integerList = new ArrayList<>(); if (str.contains("/")) { String[] stringSplit = str.split("/"); if (stringSplit.length > 2) { throw new RuntimeException(name + "格式不正確應為:y+/x+"); } for (String s : stringSplit) { maxAndMin(name, Integer.parseInt(s), max, min); } int addend = Integer.parseInt(stringSplit[0]), augend = Integer.parseInt(stringSplit[1]), and = addend + augend; integerList.add(addend); while (and < max) { integerList.add(and); and += augend; } } else if (str.contains("-")) { String[] stringSplit = str.split("-"); int max1 = 0, min1 = Integer.MAX_VALUE, value = 0; for (String s : stringSplit) { value = maxAndMin(name, Integer.parseInt(s), max, min); if (max1 < value) { max1 = value; } if (min1 > value) { min1 = value; } } for (int i = min1; i <= max1; i++) { integerList.add(i); } } else if (str.contains("[") && str.contains("]")) { str = str.substring(1, str.length() - 1); String[] strings = str.split(","); for (String s : strings) { integerList.add(maxAndMin(name, Integer.parseInt(s), max, min)); } } else if (str.contains("*")) { for (int i = min; i <= max; i++) { integerList.add(i); } } else { integerList.add(maxAndMin(name, Integer.parseInt(str), max, min)); } //對資料進行排序(從小到大) Collections.sort(integerList); return integerList.size() == 0 ? null : integerList; } private static Integer maxAndMin(String name, Integer value, Integer max, Integer min) { if ((value > max) || (value < min)) { throw new RuntimeException((name != null ? name : "") + ":數值過小或過大:" + value); } return value; }
定義時間類:
/** * 時間型別類 */ public static class TimerTaskType { private Long timeValue; //間隔 private String timeCron; //cron表達是 private Date date; //時間型別 private Long number; //執行的次數 private Date runTime = new Date(); //執行時間 private Map<String, List<Integer>> cronTimeMap; //提高效率增加的數值(cron表示式用到value) private Integer[] theVariousTimeSize = new Integer[7]; private Integer[] runcronTimeMapStep = new Integer[7]; private String cronTimerString; //銷燬標識(當時間有問題時) private Boolean theDestructionFlag = false; public TimerTaskType() { } public TimerTaskType(Long timeValue, Long number) { this.timeValue = timeValue; this.number = number; updateTime(); } public TimerTaskType(String timeCron, Long number) { this.timeCron = timeCron; this.number = number; cronTimeMap = parsingCron(timeCron); cornInit(); } public TimerTaskType(Date date, Long number) { this.date = date; this.number = number; if (!objectNoNull(this.number)) { this.number = 1L; } updateTime(); } public static TimerTaskType getTimerTaskType(Long timeValue, Long number) { return new TimerTaskType(timeValue, number); } public static TimerTaskType getTimerTaskType(String timeCron, Long number) { return new TimerTaskType(timeCron, number); } public static TimerTaskType getTimerTaskType(Date date, Long number) { return new TimerTaskType(date, number); } public Long getTimeValue() { return timeValue; } public Boolean getTheDestructionFlag() { return theDestructionFlag; } /** * cron表示式初始化 */ public void cornInit() { List<Integer> secondList = cronTimeMap.get("秒"); List<Integer> minuteList = cronTimeMap.get("分"); List<Integer> hourOfDayList = cronTimeMap.get("時"); List<Integer> dayOfMonthList = cronTimeMap.get("日"); List<Integer> monthList = cronTimeMap.get("月"); List<Integer> dayOfWeekInMonthList = cronTimeMap.get("星期"); List<Integer> yearList = cronTimeMap.get("年"); theVariousTimeSize[0] = secondList.size(); theVariousTimeSize[1] = minuteList.size(); theVariousTimeSize[2] = hourOfDayList.size(); theVariousTimeSize[3] = dayOfMonthList.size(); theVariousTimeSize[4] = monthList.size(); theVariousTimeSize[5] = dayOfWeekInMonthList.size(); theVariousTimeSize[6] = yearList.size(); // System.out.println("theVariousTimeSize:" + Arrays.toString(theVariousTimeSize)); // System.out.println("================================================="); // System.out.println("當前時間為:" + oftenDateToStringMs(new Date())); // System.out.println("================================================="); Calendar calendar = Calendar.getInstance(); //賦值runcronTimeMapStep writeRuncronTimeMapStep(secondList, calendar.get(Calendar.SECOND), 0); writeRuncronTimeMapStep(minuteList, calendar.get(Calendar.MINUTE), 1); writeRuncronTimeMapStep(hourOfDayList, calendar.get(Calendar.HOUR_OF_DAY), 2); writeRuncronTimeMapStep(dayOfMonthList, calendar.get(Calendar.DAY_OF_MONTH), 3); writeRuncronTimeMapStep(monthList, calendar.get(Calendar.MONTH) + 1, 4); writeRuncronTimeMapStep(yearList, calendar.get(Calendar.YEAR), 6); theDateOfAndWeek(); runTime = cornTheAssemblyDate(); } private Integer writeRuncronTimeMapStep(List<Integer> list, int value, int index) { // System.out.println("當前值為:" + value); if (list.contains(value)) { runcronTimeMapStep[index] = list.indexOf(value); } else { for (int x = 0; x < index; x++) { runcronTimeMapStep[x] = 0; } for (int i = 0; i < theVariousTimeSize[index]; i++) { if (list.get(i) > value) { runcronTimeMapStep[index] = i; break; } } if (runcronTimeMapStep[index] == null) { if (index == 6) { this.number = 1L; runcronTimeMapStep[index] = 0; } else { runcronTimeMapStep[index] = 0; } } } return list.get(runcronTimeMapStep[index]); // System.out.println("下標" + index + "值為:" + runcronTimeMapStep[index]); } /** * 日期與星期的交集 */ public void theDateOfAndWeek() { if (theDestructionFlag) { return; } List<Integer> dayOfWeekInMonthList = cronTimeMap.get("星期"); Date date = cornTheAssemblyDate(); Calendar calendar = Calendar.getInstance(); calendar.setTime(date); int theDateOf = calendar.get(Calendar.DAY_OF_WEEK) - 1; theDateOf = theDateOf == 0 ? 7 : theDateOf; if (!dayOfWeekInMonthList.contains(theDateOf)) { theDateOfChangeMonth(); theDateOfAndWeek(); } } /** * 組裝cron * * @return */ private Date cornTheAssemblyDate() { // System.out.println("*************************************************"); List<List<Integer>> lists = new ArrayList<>(); lists.add(cronTimeMap.get("秒")); lists.add(cronTimeMap.get("分")); lists.add(cronTimeMap.get("時")); lists.add(cronTimeMap.get("日")); lists.add(cronTimeMap.get("月")); lists.add(cronTimeMap.get("星期")); lists.add(cronTimeMap.get("年")); cronTimerString = ""; for (int i = 6; i > -1; i--) { if (i != 5) { writeTeimerString(lists.get(i), i); } } // System.out.println("*************************************************"); return stringToDate("yyyy-MM-dd-HH-mm-ss", cronTimerString); } /** * * @param list * @param index */ private void writeTeimerString(List<Integer> list, int index) { String str = list.get(runcronTimeMapStep[index]).toString() + "-"; cronTimerString += str; if (index == 0) { cronTimerString = cronTimerString.substring(0, cronTimerString.length() - 1); System.out.println("cronTimerString時間:" + cronTimerString); } } /** * 日,月,年處理 */ public void theDateOfChangeMonth() { //日 runcronTimeMapStep[3]++; runcronTimeMapStep[2] = 0; runcronTimeMapStep[1] = 0; runcronTimeMapStep[0] = 0; if (runcronTimeMapStep[3] >= theVariousTimeSize[3]) { //月 runcronTimeMapStep[4]++; runcronTimeMapStep[3] = 0; if (runcronTimeMapStep[4] >= theVariousTimeSize[4]) { //年 runcronTimeMapStep[6]++; runcronTimeMapStep[4] = 0; if (runcronTimeMapStep[6] >= theVariousTimeSize[6]) { //防止發生錯誤 runcronTimeMapStep[6] = 0; number = 1L; //銷燬標準 theDestructionFlag = true; System.out.println("年的值達到上限,或月和星期有衝突"); } } } } /** * 返回cron時間 * * @return */ public Date getCornToTime() { //秒 runcronTimeMapStep[0]++; if (runcronTimeMapStep[0] >= theVariousTimeSize[0]) { //分 runcronTimeMapStep[1]++; runcronTimeMapStep[0] = 0; if (runcronTimeMapStep[1] >= theVariousTimeSize[1]) { //時 runcronTimeMapStep[2]++; runcronTimeMapStep[1] = 0; if (runcronTimeMapStep[2] >= theVariousTimeSize[2]) { theDateOfChangeMonth(); theDateOfAndWeek(); } } } return cornTheAssemblyDate(); } public Date getDate() { return date; } public Long getNumber() { return number; } /** * 返回執行時間 * * @return */ public Date getRunTime() { return this.runTime; } /** * 更新時間 */ public void updateTime() { if (objectNoNull(timeValue)) { this.runTime = new Date(runTime.getTime() + timeValue); } else if (objectNoNull(timeCron)) { this.runTime = getCornToTime(); } else { this.runTime = date; } } /** * 次數執行是否到0 * * @return */ public Boolean getYesAndNoRun() { if (objectNoNull(number)) { if ((--number) == 0) { return true; } } return false; } @Override public String toString() { return "TimerTaskType{" + "timeValue=" + timeValue + ", timeCron='" + timeCron + '\'' + ", date=" + date + ", number=" + number + '}'; } }
定義執行任務類:
/**
* 外部的任務類,要使用類繼承字本類後才能用做著個工具任務
*/
public static abstract class TimerRunClass {
//一些儲存資料的變數,當不用存值是可以無視
protected String stringValue;
protected Long longValue;
protected Integer intValue;
protected List<Object> objectListVlaue;
protected Object objectValue;
//執行這次任務的時間
protected Date runTime;
public String getStringValue() {
return stringValue;
}
public Long getLongValue() {
return longValue;
}
public Integer getIntValue() {
return intValue;
}
public List<Object> getObjectListVlaue() {
return objectListVlaue;
}
public Object getObjectValue() {
return objectValue;
}
/**
* 組裝值
*
* @param obj
*/
public final void setAllTypeValue(Object obj) {
if (obj instanceof String) {
this.stringValue = (String) obj;
} else if (obj instanceof Long) {
this.longValue = (Long) obj;
} else if (obj instanceof List) {
objectListVlaue = (List<Object>) obj;
} else if (obj instanceof Integer) {
intValue = (Integer) obj;
} else {
objectValue = obj;
}
}
public void setStringValue(String stringValue) {
this.stringValue = stringValue;
}
public void setLongValue(Long longValue) {
this.longValue = longValue;
}
public void setIntValue(Integer intValue) {
this.intValue = intValue;
}
public void setObjectListVlaue(List<Object> objectListVlaue) {
this.objectListVlaue = objectListVlaue;
}
public void setObjectValue(Object objectValue) {
this.objectValue = objectValue;
}
public void setRunTime(Date runTime) {
this.runTime = runTime;
}
/**
* 時間執行任務函式
*/
public abstract void run();
/**
* 修改當前任務時間
*
* @return
*/
public TimerTaskType modifyTheTimeTask() {
return null;
}
/**
* 新增時間
*
* @return
*/
public TaskSchedulingClass addTimeTask() {
return null;
}
}
定義工具類使用的任務類:
/**
* 任務排程物件
*/
public static class TaskSchedulingClass implements Comparable<TaskSchedulingClass> {
private String thisaName;
private TimerTaskType timerTaskType;
private TimerRunClass timerRunClass;
public TaskSchedulingClass() {
}
public TaskSchedulingClass(String thisaName, TimerTaskType timerTaskType, TimerRunClass timerRunClass) {
this.thisaName = thisaName;
this.timerTaskType = timerTaskType;
this.timerRunClass = timerRunClass;
}
public String getThisaName() {
return thisaName;
}
public TimerTaskType getTimerTaskType() {
return timerTaskType;
}
public TimerRunClass getTimerRunClass() {
return timerRunClass;
}
public void setThisaName(String thisaName) {
this.thisaName = thisaName;
}
public void setTimerTaskType(TimerTaskType timerTaskType) {
this.timerTaskType = timerTaskType;
}
public void setTimerRunClass(TimerRunClass timerRunClass) {
this.timerRunClass = timerRunClass;
}
/**
* 執行
*/
public void run() {
this.timerRunClass.run();
TimerTaskType timerTaskType = this.timerRunClass.modifyTheTimeTask();
if (objectNoNull(timerTaskType)) {
if (objectNoNull(timerTaskType.number)) {
timerTaskType.number++;
}
this.timerTaskType = timerTaskType;
}
TaskSchedulingClass taskSchedulingClass = this.timerRunClass.addTimeTask();
if (objectNoNull(taskSchedulingClass)) {
addTaskScheduling(taskSchedulingClass);
}
if (this.timerTaskType.getYesAndNoRun() || this.timerTaskType.getTheDestructionFlag()) {
System.out.println("自銷燬");
taskSchedulingList.remove(this);
} else {
this.timerTaskType.updateTime();
}
}
@Override
public int compareTo(TaskSchedulingClass taskSchedulingClass) {
if (this == taskSchedulingClass) {
return 0;
} else {
return ((Long) (this.timerTaskType.getRunTime().getTime() - taskSchedulingClass.timerTaskType.getRunTime().getTime())).intValue();
}
}
}
到這裡就可以寫工具類了:
public class TimerUtils {
//任務排程集合
private static List<TaskSchedulingClass> taskSchedulingList = new ArrayList<TaskSchedulingClass>();
//唯一的供任務呼叫的定時器
private static Timer taskTimer = new Timer();
//要執行的任務
private static List<TaskSchedulingClass> theRunTasks;
//要執行的時間
private static Date runTime;
private static Boolean runFlag = false;
private String test;
/**
* 增加一個任務
*
* @param timeName
* @param timerTaskType
* @param timerRunClass
* @return
*/
public static synchronized Boolean addTaskScheduling(String timeName, TimerTaskType timerTaskType, TimerRunClass timerRunClass) {
return addTaskScheduling(new TaskSchedulingClass(timeName, timerTaskType, timerRunClass));
}
/**
* 增加一個任務
*
* @param taskSchedulingClass
* @return
*/
public static synchronized Boolean addTaskScheduling(TaskSchedulingClass taskSchedulingClass) {
if (nameYesAndNoThereAre(taskSchedulingClass.getThisaName())) {
System.out.println("此定時器已存在,新增無效");
return false;
}
if (taskSchedulingClass.getTimerTaskType().getTheDestructionFlag()) {
System.out.println("此任務時間類銷燬標誌打開了,無法新增任務");
return false;
}
taskSchedulingList.add(taskSchedulingClass);
if (runFlag) {
if (runTime.getTime() > taskSchedulingClass.getTimerTaskType().getRunTime().getTime()) {
newTaskTimer();
startTaskTimer();
}
} else {
startTaskTimer();
runFlag = true;
}
return true;
}
/**
* 檢測這樣的定時器是否存在
*
* @param name
* @return
*/
public static Boolean nameYesAndNoThereAre(String name) {
for (TaskSchedulingClass t : taskSchedulingList) {
if (t.getThisaName().equals(name)) {
return true;
}
}
return false;
}
/**
* 更新
*
* @param name
* @param timerTaskType
* @return
*/
public static Boolean updateNmaeTimerTime(String name, TimerTaskType timerTaskType) {
System.out.println("更新:" + name);
for (TaskSchedulingClass t : taskSchedulingList) {
if (t.getThisaName().equals(name)) {
t.setTimerTaskType(timerTaskType);
//如果時間小於執行時間就進行排程更新
if ((runTime.getTime() > timerTaskType.getRunTime().getTime()) || (theRunTasks.contains(t))) {
newTaskTimer();
startTaskTimer();
}
return true;
}
}
return false;
}
/**
* 啟動定時器
*/
public static void startTaskTimer() {
if (taskSchedulingList.size() > 0) {
forTaskForScheduling();
if (taskSchedulingList.size() > 0) {
taskTimer.schedule(getTimerTask(theRunTasks), runTime);
}
} else {
stopTaskTimer();
}
}
/**
* 更新執行任務
*/
public static void newTaskTimer() {
runTime = null;
theRunTasks = null;
stopTaskTimer();
}
/**
* 停止定時器
*/
public static void stopTaskTimer() {
taskTimer.cancel();
taskTimer = new Timer();
runFlag = false;
}
/**
* 清除所有任務
*/
public static void deleteAllTask() {
taskSchedulingList = new ArrayList<TaskSchedulingClass>();
}
/**
* 任務排程
*
* @return
*/
public static Boolean forTaskForScheduling() {
List<TaskSchedulingClass> taskList = new ArrayList<TaskSchedulingClass>();
TaskSchedulingClass min = Collections.min(taskSchedulingList);
runTime = min.getTimerTaskType().getRunTime();
Iterator<TaskSchedulingClass> iterator = taskSchedulingList.iterator();
while (iterator.hasNext()) {
TaskSchedulingClass next = iterator.next();
if (next.compareTo(min) == 0) {
taskList.add(next);
}
}
System.out.println("定時器裡還有:" + taskSchedulingList.size() + "個任務");
//向執行類裡寫入執行時間
if (taskList.size() != 0) {
for (TaskSchedulingClass t : taskList) {
t.getTimerRunClass().setRunTime(runTime);
}
}
theRunTasks = taskList;
return true;
}
/**
* 返回一個TimerTask供Timer呼叫
*
* @param taskSchedulingClasss
* @return
*/
public static synchronized TimerTask getTimerTask(final List<TaskSchedulingClass> taskSchedulingClasss) {
return new TimerTask() {
@Override
public void run() {
// System.out.println(taskSchedulingClasss.size());
//這裡的taskSchedulingClasss在java1.8以前的要加final關鍵字
for (TaskSchedulingClass t : taskSchedulingClasss) {
t.run();
}
startTaskTimer();
}
};
}
private static Boolean objectNoNull(Object obj) {
if (obj != null) {
return true;
}
return false;
}
/**
* 以下是附加的方便時間格式化顯示
*/
/**
* 常用
*
* @param string
* @return
*/
private static Long oftenStringToTimeMs(String string) {
return stringToTime("yyyy-MM-dd HH:mm:ss", string);
}
private static String oftenDateToStringMs(Date date) {
return dateToString("yyyy-MM-dd HH:mm:ss", date);
}
/**
* 字串格式時間轉毫秒值
*
* @param format
* @param value
* @return
*/
public static Long stringToTime(String format, String value) {
return stringToDate(format, value).getTime();
}
/**
* 字串格式時間轉Date
*
* @param format
* @param value
* @return
*/
public static Date stringToDate(String format, String value) {
SimpleDateFormat sdft = new SimpleDateFormat(format);
Date date = null;
try {
date = sdft.parse(value);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
/**
* Date時間轉字串
*
* @param format
* @param date
* @return
*/
public static String dateToString(String format, Date date) {
SimpleDateFormat sdft = new SimpleDateFormat(format);
String str = null;
str = sdft.format(date);
return str;
}
}
到此處工具類就寫完了。
執行例子:
定義一個執行類繼承TimerRunClass:
public class MyRun extends TimerUtils.TimerRunClass {
private String name;
private int i = 0;
public MyRun() {
}
public MyRun(String name) {
this.name = name;
}
@Override
public void run() {
i++;
System.out.println(i + "執行:" + name);
}
}
寫一個main函式:
public class Demo {
public static void main(String[] args) throws InterruptedException {
TimerUtils.TimerTaskType timerTaskType = TimerUtils.TimerTaskType.getTimerTaskType("[10,20,30,40,50] * * * * * *", null);
TimerUtils.addTaskScheduling("訂單", timerTaskType, new MyRun("程式"));
}
}
執行效果如下:
stringList:[[10,20,30,40,50], *, *, *, *, *, *]
秒 :[10,20,30,40,50]
分 :*
時 :*
日 :*
月 :*
星期 :*
年 :*
{秒=[10, 20, 30, 40, 50], 年=[1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028, 2029, 2030, 2031, 2032, 2033, 2034, 2035, 2036, 2037, 2038, 2039, 2040, 2041, 2042, 2043, 2044, 2045, 2046, 2047, 2048, 2049, 2050, 2051, 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2067, 2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2076, 2077, 2078, 2079, 2080, 2081, 2082, 2083, 2084, 2085, 2086, 2087, 2088, 2089, 2090, 2091, 2092, 2093, 2094, 2095, 2096, 2097, 2098, 2099], 日=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31], 分=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59], 時=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23], 月=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 星期=[1, 2, 3, 4, 5, 6, 7]}
cronTimerString時間:2018-1-4-23-16-20
cronTimerString時間:2018-1-4-23-16-20
定時器裡還有:1個任務
1執行:程式
cronTimerString時間:2018-1-4-23-16-30
定時器裡還有:1個任務
2執行:程式
cronTimerString時間:2018-1-4-23-16-40
定時器裡還有:1個任務
3執行:程式
cronTimerString時間:2018-1-4-23-16-50
定時器裡還有:1個任務
4執行:程式
到此,這篇文章就完了!!