Cron表達式解析器詳解

一、Cron表達式生成器

Cron表達式是一種在定時任務中經常使用的語法,它可以用來指定被執行的時間,像“每小時執行一次”或“每周星期五晚上10點執行”等等。由於Cron表達式的語法比較複雜,因此通常需要使用Cron表達式生成器來幫助用戶快速生成想要的表達式。

下面是一個簡單的Cron表達式生成器的示例,它可以讓用戶選擇任務執行的時間單位、時間點和重複頻率,然後根據用戶的選擇生成對應的Cron表達式:

function generateCronExpression() {
  // 獲取用戶的選擇
  let timeUnit = document.getElementById("time-unit-select").value;
  let timePoint = document.getElementById("time-point-input").value;
  let repeatFrequency = document.getElementById("repeat-frequency-input").value;
  
  // 根據用戶選擇生成Cron表達式
  let cronExp = "";
  if (timeUnit === "hourly") {
    cronExp = `0 ${timePoint} 0/${repeatFrequency} * * *`;
  } else if (timeUnit === "daily") {
    cronExp = `0 ${timePoint} * * * *`;
  } else if (timeUnit === "weekly") {
    cronExp = `0 ${timePoint} * * ${repeatFrequency} *`;
  } else if (timeUnit === "monthly") {
    cronExp = `0 ${timePoint} ${repeatFrequency} * * *`;
  }
  
  document.getElementById("result").innerHTML = cronExp;
}

二、Cron表達式解析成時間

除了生成Cron表達式之外,我們還需要將Cron表達式解析成對應的時間點,以便於查看任務執行的計劃。下面是一個簡單的解析器,它可以將Cron表達式解析成下一次執行的時間點:

function getNextExecutionTime(cronExp) {
  let cronArr = cronExp.split(" ");
  let now = Date.now();
  
  // 解析分鐘表達式
  let minuteExp = cronArr[0];
  let nextMinute = getNextValidValue(minuteExp, new Date(now).getMinutes());
  
  // 解析小時表達式
  let hourExp = cronArr[1];
  let nextHour = getNextValidValue(hourExp, new Date(now).getHours());
  
  // 解析日表達式
  let dayExp = cronArr[2];
  let nextDay = getNextValidValue(dayExp, new Date(now).getDate());
  
  // 解析月表達式
  let monthExp = cronArr[3];
  let nextMonth = getNextValidValue(monthExp, new Date(now).getMonth() + 1);
  
  // 解析星期表達式
  let weekExp = cronArr[4];
  let nextWeekDay = getNextValidValue(weekExp, new Date(now).getDay());
  
  // 解析年表達式
  let yearExp = cronArr[5];
  let nextYear = getNextValidValue(yearExp, new Date(now).getFullYear());
  
  // 計算下一次執行的時間點(注意要考慮到月份和年份的變化)
  let nextDate = new Date(nextYear, nextMonth - 1, nextDay, nextHour, nextMinute);
  while (nextDate.getTime()  12) {
      nextMonth = 1;
      nextYear++;
    }
    nextDate.setFullYear(nextYear, nextMonth - 1, nextDay);
  }
  
  return nextDate;
}

function getNextValidValue(exp, currentValue) {
  // TODO:根據表達式解析出下一個合法的值
  // 略去具體實現
  return 0;
}

三、Cron表達式在線解析

為了方便用戶查詢指定Cron表達式的下一次執行時間點,我們可以提供一個在線的解析工具,用戶只需要將表達式粘貼到輸入框中,即可得到下一次的執行時間:

function parseCronExpression() {
  let cronExp = document.getElementById("cron-exp-input").value;
  let nextExecutionTime = getNextExecutionTime(cronExp);
  document.getElementById("next-execution-time").innerHTML = nextExecutionTime;
}

四、Cron表達式每5分鐘

有時候我們需要將任務設置成每5分鐘一次執行,這時候就需要使用*/5來指定:

0 */5 * * * *

五、Cron表達式解析

對於Cron表達式的解析,其實就是將表達式的各個部分解析出來,並根據這些部分計算下一次的執行時間點。下面是一個完整的Cron表達式解析器的實現,它可以解析所有Cron表達式,並計算出下一次的執行時間點:

function parseCronExpression(cronExp) {
  let cronArr = cronExp.split(" ");
  let now = Date.now();
  
  // 解析分鐘表達式
  let minuteExp = cronArr[0];
  let nextMinute = getNextValidValues(minuteExp, new Date(now).getMinutes());
  
  // 解析小時表達式
  let hourExp = cronArr[1];
  let nextHour = getNextValidValues(hourExp, new Date(now).getHours());
  
  // 解析日表達式
  let dayExp = cronArr[2];
  let nextDay = getNextValidValues(dayExp, new Date(now).getDate());
  
  // 解析月表達式
  let monthExp = cronArr[3];
  let nextMonth = getNextValidValues(monthExp, new Date(now).getMonth() + 1);
  
  // 解析星期表達式
  let weekExp = cronArr[4];
  let nextWeekDay = getNextValidValues(weekExp, new Date(now).getDay());
  
  // 解析年表達式
  let yearExp = cronArr[5];
  let nextYear = getNextValidValues(yearExp, new Date(now).getFullYear());
  
  let nextExecutionTimes = [];
  
  for (let i = 0; i < nextMinute.length; i++) {
    for (let j = 0; j < nextHour.length; j++) {
      for (let k = 0; k < nextDay.length; k++) {
        for (let l = 0; l < nextMonth.length; l++) {
          let date = new Date(nextYear[0], nextMonth[l] - 1, nextDay[k], nextHour[j], nextMinute[i]);
          while (date.getTime() < now) {
            // 如果時間已經過去了,則需要加上一個周期
            if (monthExp === "*" && weekExp === "*") {
              // 每天執行的任務
              date.setDate(date.getDate() + 1);
            } else if (monthExp === "*" && weekExp !== "*") {
              // 每周某一天執行的任務
              date.setDate(date.getDate() + 7);
            } else if (monthExp !== "*" && weekExp === "*") {
              // 每月某一天執行的任務
              let tempDate = new Date(date.getFullYear(), date.getMonth() + 1, 0);
              date.setDate(tempDate.getDate() + 1);
              if (date.getMonth() + 1 !== nextMonth[l]) {
                // 某個月沒有對應日期,需要調整到下一個合法日期
                date.setDate(1);
                date.setMonth(date.getMonth() + 1);
              }
            } else {
              // 每年某一天執行的任務
              let tempDate = new Date(date.getFullYear(), nextMonth[l], 0);
              date.setDate(tempDate.getDate() + 1);
              if (date.getMonth() + 1 !== nextMonth[l] || date.getDay() !== nextWeekDay[0]) {
                // 某個月沒有對應的星期,或者對應的星期不是指定的星期,需要調整到下一個合法日期
                date.setDate(1);
                date.setMonth(date.getMonth() + 1);
                while (date.getDay() !== nextWeekDay[0]) {
                  date.setDate(date.getDate() + 1);
                }
              }
            }
          }
          
          nextExecutionTimes.push(date);
        }
      }
    }
  }
  
  return nextExecutionTimes;
}

function getNextValidValues(exp, currentValue) {
  let result = [];
  if (exp === "*") {
    // 如果表達式為*,則返回對應取值範圍內的所有值
    for (let i = getMinValue(exp); i <= getMaxValue(exp); i++) {
      result.push(i);
    }
  } else if (exp.indexOf("/") !== -1) {
    // 如果表達式中包含/,則按照步長來獲取合法值
    let arr = exp.split("/");
    let startValue = getMinValue(arr[0]);
    let step = parseInt(arr[1]);
    for (let i = startValue; i <= getMaxValue(exp); i += step) {
      result.push(i);
    }
  } else if (exp.indexOf(",") !== -1) {
    // 如果表達式中包含,,則將其拆分成多個子表達式
    let arr = exp.split(",");
    for (let i = 0; i < arr.length; i++) {
      result = result.concat(getNextValidValues(arr[i], currentValue));
    }
    result = Array.from(new Set(result)); // 去重
    result.sort(function(a, b) { return a - b; }); // 排序
  } else if (exp.indexOf("-") !== -1) {
    // 如果表達式中包含-,則按照範圍來獲取合法值
    let arr = exp.split("-");
    let minValue = parseInt(arr[0]);
    let maxValue = parseInt(arr[1]);
    for (let i = minValue; i <= maxValue; i++) {
      result.push(i);
    }
  } else {
    // 如果表達式為單個數值,則直接返回該值
    result.push(parseInt(exp));
  }
  
  // 對於星期表達式,還需要特殊處理,因為星期的取值範圍是0~6,但是Date對象中的星期是從0開始的
  // 因此需要將所有星期的值+1,如果越界了則取模
  if (exp.startsWith("0") && result.indexOf(7) !== -1) {
    result.push(0);
  } else if (exp.startsWith("7") && result.indexOf(0) !== -1) {
    result.push(7);
  }
  result = result.map(function(value) { return (value === 7 ? 0 : value + 1); });
  for (let i = 0; i < result.length; i++) {
    result[i] %= 7;
  }
  
  // 如果當前值不在合法值的範圍內,需要找到比它大的第一個合法值
  let index = result.indexOf(currentValue);
  if (index === -1) {
    for (let i = 0; i  currentValue) {
        index = i;
        break;
      }
    }
  }
  
  // 如果當前值在合法值的範圍內,直接從它開始向後找到下一個合法值
  if (index !== -1) {
    for (let i = index; i = currentValue) {
        result = result.slice(i);
        break;
      }
    }
  }
  
  // 如果找不到合法值,說明下一次執行時間已經超過了指定的時間範圍,這時候需要將結果清空
  if (result.length === 0) {
    result.push(getMinValue(exp));
  }
  
  return result;
}

function getMinValue(exp) {
  // TODO:根據表達式獲取取值範圍的最小值
  // 略去具體實現
  return 0;
}

function getMaxValue(exp) {
  // TODO:根據表達式獲取取值範圍的最大值
  // 略去具體實現
  return 59;
}

六、Cron表達式在線生成器

除了簡單的Cron表達式生成器之外,我們還可以提供一個功能更加強大的在線生成器,讓用戶可以通過界面來選擇更加複雜的任務執行計劃:

// 界面部分
// 略...

// 事件處理部分
function generateCronExpression() {
let minuteExp = getCronExp("minute");
let hourExp = getCronExp("hour");
let dayExp = getCronExp("day");
let monthExp = getCronExp("month");
let weekExp = getCronExp("week");

let

原創文章,作者:OOOV,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/145245.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
OOOV的頭像OOOV
上一篇 2024-10-27 23:47
下一篇 2024-10-27 23:48

相關推薦

  • Cron執行日誌用法介紹

    本文將從多個方面對cron執行日誌進行詳細闡述,包括cron執行日誌的定義、cron執行日誌的產生原因、cron執行日誌的格式以及如何解讀cron執行日誌。 一、定義 Cron是一…

    編程 2025-04-29
  • 神經網絡代碼詳解

    神經網絡作為一種人工智能技術,被廣泛應用於語音識別、圖像識別、自然語言處理等領域。而神經網絡的模型編寫,離不開代碼。本文將從多個方面詳細闡述神經網絡模型編寫的代碼技術。 一、神經網…

    編程 2025-04-25
  • Linux sync詳解

    一、sync概述 sync是Linux中一個非常重要的命令,它可以將文件系統緩存中的內容,強制寫入磁盤中。在執行sync之前,所有的文件系統更新將不會立即寫入磁盤,而是先緩存在內存…

    編程 2025-04-25
  • Java BigDecimal 精度詳解

    一、基礎概念 Java BigDecimal 是一個用於高精度計算的類。普通的 double 或 float 類型只能精確表示有限的數字,而對於需要高精度計算的場景,BigDeci…

    編程 2025-04-25
  • Python安裝OS庫詳解

    一、OS簡介 OS庫是Python標準庫的一部分,它提供了跨平台的操作系統功能,使得Python可以進行文件操作、進程管理、環境變量讀取等系統級操作。 OS庫中包含了大量的文件和目…

    編程 2025-04-25
  • Linux修改文件名命令詳解

    在Linux系統中,修改文件名是一個很常見的操作。Linux提供了多種方式來修改文件名,這篇文章將介紹Linux修改文件名的詳細操作。 一、mv命令 mv命令是Linux下的常用命…

    編程 2025-04-25
  • nginx與apache應用開發詳解

    一、概述 nginx和apache都是常見的web服務器。nginx是一個高性能的反向代理web服務器,將負載均衡和緩存集成在了一起,可以動靜分離。apache是一個可擴展的web…

    編程 2025-04-25
  • Python輸入輸出詳解

    一、文件讀寫 Python中文件的讀寫操作是必不可少的基本技能之一。讀寫文件分別使用open()函數中的’r’和’w’參數,讀取文件…

    編程 2025-04-25
  • 詳解eclipse設置

    一、安裝與基礎設置 1、下載eclipse並進行安裝。 2、打開eclipse,選擇對應的工作空間路徑。 File -> Switch Workspace -> [選擇…

    編程 2025-04-25
  • MPU6050工作原理詳解

    一、什麼是MPU6050 MPU6050是一種六軸慣性傳感器,能夠同時測量加速度和角速度。它由三個傳感器組成:一個三軸加速度計和一個三軸陀螺儀。這個組合提供了非常精細的姿態解算,其…

    編程 2025-04-25

發表回復

登錄後才能評論