示例#1
0
文件: Task.java 项目: qiu8310/crontab
  /**
   * 计划任务在指定的时间是否能运行
   *
   * @param timeInMillisecond
   * @return
   */
  public Boolean canRunAt(long timeInMillisecond) {

    List<Integer> numbers;
    Map<String, Integer> time;

    time = Helper.justifyCalendar(timeInMillisecond);

    for (String key : this.allowedFieldNumbers.keySet()) {
      numbers = this.allowedFieldNumbers.get(key);
      // System.out.println(field + ": " + numbers.toString());
      if (!numbers.contains(time.get(key))) return false;
    }
    return true;
  }
示例#2
0
文件: Task.java 项目: qiu8310/crontab
  /**
   * 根据当前运行时间计算出此计划任务下次运行的时间(运行时间不考虑秒和毫秒,不管它们是不是0,都把它当作0)
   *
   * <p>思路: 计划任务中有周,没有年,关系比较复杂。但我们可以分开来考虑,分、时的变化不影响周。要算周时,可以动态高度年去得到一个合适的周
   *
   * <p>算法: 1、因为当前时间是可运行时间,所以保持日、月、周不变,只改变分、时,看能否得出一个比当前时间大的值,能的话这个就是下次运行时间
   * 2、第1步没取到,则分别取分、时的最小值,并保持不变,改变日、月、年(不是周),来计算出下次运行时间 => 这一步的具体算法放在 figureOutNextRunTimeByDMY 函数中
   *
   * @return
   * @throws Exception
   */
  private long figureOutNextRunTimeByRunTime(long timeInMillisecond) throws Exception {
    // 当前指定的时间的组合
    Map<String, Integer> timeMap = Helper.justifyCalendar(timeInMillisecond);

    Boolean got = false; // 是否找到了一个合适的值
    String[] keys = {"minute", "hour"}; // 循环用的关键字

    Integer currTime; // 当前指定的时/分
    List<Integer> allowTimes; // 所有允许的时/分值
    Integer allowSize, // 所有允许的时/分值的总个数
        index; // 指定的 时/分 在允许的 时/分 中的索引

    for (String key : keys) {

      currTime = timeMap.get(key);
      allowTimes = allowedFieldNumbers.get(key);

      // 上一个时间字段比指定的大,下面的字段只要保持和指定的相等,就可以保证是最临近的下次运行时间
      if (got) {
        timeMap.put(key, currTime);
        continue;
      }

      index = allowTimes.indexOf(currTime);
      allowSize = allowTimes.size();

      // 指定的时间是最大值
      if (index + 1 == allowSize) {
        timeMap.put(key, allowTimes.get(0)); // 没有比当前时间大的,只能取最小的值了
      } else {
        got = true;
        timeMap.put(key, allowTimes.get(index + 1));
      }
    }

    if (got) {
      return Helper.reverseJustifyCalendar(timeMap);
    } else {
      return this.figureOutNextRunTimeByDMY(timeMap);
    }
  }
示例#3
0
文件: Task.java 项目: qiu8310/crontab
  /**
   * 根据任意时间来获取计划任务的下次运行的时间
   *
   * <p>思路: 得到一个最小的 月、日、时、分 组合,使得它可以匹配计划任务的 月、日、时、分, 再判断下它的周符不符合条件,如果符合则返回结果;如果不符合, 则将时、分设置成最小值,再扔给
   * figureOutNextRunTimeByDMY 处理。
   *
   * <p>所以此处算法关键是如何根据任意一个时间得到这个最小的月、日、时、分组合
   *
   * <p>算法: 设置 got = false(表示:是否找到了一个比指定时间大的值,如果为true预示着之后的所有字段都取最小值) 设置 back = false
   * (表示:按当前遍历的字段是否是从下一个字段回退过来的,回退过来同时表示之前保存的值需要加1码) 1、按 month、day、hour、minute 的顺序遍历 2、修正:当前遍历的是否是
   * day,是day的话需要根据当前的年份和月份,算出最大允许的day,和计划的所有允许的day取个交集
   * 如果这个交集是个空集(每月的最大天数会变化,所以对于那些日数设置成31或30的偶尔会出现空集), 回退 back = true 3、 如果 got = true 取当前允许的最小值 如果
   * back = true 取出之前此字段保存的值,看还能否加一码 如果能加,此字段设置成加一码后的值,got = true 如果不能加 => 当前字段是否无法再后退了 ? 退出循环 :
   * 继续后退 back = true got = false && back = false 如果能取到和指定值相等的值,则保存此值,继续遍历 如果所有值都小于指定值, back = true
   * 剩下的情况,取一个正好比指定值大的值,保存下来, got = true
   *
   * <p>4、退出遍历后 这样得到了一个最小的 month、day、hour、minute组合,可以符合正好比指定时间大,同时满足当前计划任务的对这四个值的要求 再结合指定的
   * year,看下这个组合得到的 week 是否符合计划任务要求 符合的话就返回这个值 不符合就取 hour、minute的最小值,同时交给之前写的另一个算法:处理 year month day
   * 和 week 之前的关系
   *
   * @param timeInMillisecond
   * @return
   * @throws Exception
   */
  private long figureOutNextRunTimeByAnyTime(long timeInMillisecond) throws Exception {
    // 可以用上面的算法就用上面的
    if (this.canRunAt(timeInMillisecond)) {
      return this.figureOutNextRunTimeByRunTime(timeInMillisecond);
    }

    Map<String, Integer> timeMap = Helper.justifyCalendar(timeInMillisecond);

    String[] keys = {"month", "day", "hour", "minute"};
    String key;
    Integer keyIdx;

    List<Integer> allows; // 所有允许的时间值
    Integer allowSize, allowMin, allowMax, currVal, currIdx;
    Boolean got = false, back = false;
    for (keyIdx = 0; keyIdx < keys.length; keyIdx++) {
      key = keys[keyIdx];

      allows = this.allowedFieldNumbers.get(key);

      // 根据年份月份修改 days
      if (key.equals("day") && allows.get(allows.size() - 1) > 28) {
        allows = this.reviseAllowDays(allows, timeMap);
        if (allows.size() == 0) {
          back = true;
          keyIdx = keyIdx - 2; // 当前 keyIdx = 1,所以肯定可以后退,减2后再循环的时候会加1,所以不用担心keyIdx = -1
          continue;
        }
      }

      allowSize = allows.size();
      allowMin = allows.get(0);
      allowMax = allows.get(allowSize - 1);
      currVal = timeMap.get(key);
      currIdx = allows.indexOf(currVal);
      ;
      if (got == true) {
        timeMap.put(key, allowMin);
      } else if (back == true) {
        if (currVal < allowMax) {
          // 取出一个比当前大的值放到 timeMap 中
          for (int i : allows) {
            if (i > currVal) {
              timeMap.put(key, i);
              break;
            }
          }
          got = true;
          back = false;
          continue;

        } else {
          // System.out.println(keyIdx);
          // 回退到了最顶层,退出循环
          if (keyIdx == 0) {
            break;
            // 继续回退
          } else {
            back = true;
            keyIdx = keyIdx - 2;
            continue;
          }
        }
      } else {
        // 如果能取到和指定值相等的值,则保存此值,继续遍历 back = false
        // 如果所有值都小于指定值, back = true
        // 剩下的情况,取一个正好比指定值大的值,保存下来, got = true
        if (currIdx != -1) {
          timeMap.put(key, currVal);
          back = false;
          continue;
        } else if (allowMax < currVal) {
          back = true;
          keyIdx = keyIdx - 2;
          continue;
        } else {
          for (int j : allows) {
            if (j > currVal) {
              timeMap.put(key, j);
              break;
            }
          }
          got = true;
        }
      }
    }

    // 这样得到了一个最小的 month、day、hour、minute组合,可以符合正好比指定时间大,同时满足当前计划任务的对这四个值的要求
    // 再结合指定的 year,看下这个组合得到的 week 是否符合计划任务要求
    // 符合的话就返回这个值
    // 不符合就取 hour、minute的最小值,同时交给之前写的另一个算法:处理 year month day 和 week 之前的关系
    if (got == true) {
      long milliSeconds = Helper.reverseJustifyCalendar(timeMap);
      if (this.canRunAt(milliSeconds)) {
        return milliSeconds;
      }
    }
    timeMap.put("minute", this.allowedFieldNumbers.get("minute").get(0));
    timeMap.put("hour", this.allowedFieldNumbers.get("hour").get(0));
    // System.out.println(timeMap);
    return this.figureOutNextRunTimeByDMY(timeMap);
  }