public synchronized Date getTimeAfter()

in core/src/main/java/com/jetbrains/youtrackdb/internal/core/schedule/CronExpression.java [1092:1489]


  public synchronized Date getTimeAfter(Date afterTime) {

    // Computation is based on Gregorian year only.
    Calendar cl = new java.util.GregorianCalendar(getTimeZone());

    // move ahead one second, since we're computing the time *after* the
    // given time
    afterTime = new Date(afterTime.getTime() + 1000);
    // CronTrigger does not deal with milliseconds
    cl.setTime(afterTime);
    cl.set(Calendar.MILLISECOND, 0);

    var gotOne = false;
    // loop until we've computed the next time, or we've past the endTime
    while (!gotOne) {

      // if (endTime != null && cl.getTime().after(endTime)) return null;
      if (cl.get(Calendar.YEAR) > 2999) { // prevent endless loop...
        return null;
      }

      IntSortedSet st;
      var t = 0;

      var sec = cl.get(Calendar.SECOND);
      var min = cl.get(Calendar.MINUTE);

      // get second.................................................
      st = seconds.tailSet(sec);
      if (st != null && !st.isEmpty()) {
        sec = st.first();
      } else {
        sec = seconds.first();
        min++;
        cl.set(Calendar.MINUTE, min);
      }
      cl.set(Calendar.SECOND, sec);

      min = cl.get(Calendar.MINUTE);
      var hr = cl.get(Calendar.HOUR_OF_DAY);
      t = -1;

      // get minute.................................................
      st = minutes.tailSet(min);
      if (st != null && st.size() != 0) {
        t = min;
        min = st.first();
      } else {
        min = minutes.first();
        hr++;
      }
      if (min != t) {
        cl.set(Calendar.SECOND, 0);
        cl.set(Calendar.MINUTE, min);
        setCalendarHour(cl, hr);
        continue;
      }
      cl.set(Calendar.MINUTE, min);

      hr = cl.get(Calendar.HOUR_OF_DAY);
      var day = cl.get(Calendar.DAY_OF_MONTH);
      t = -1;

      // get hour...................................................
      st = hours.tailSet(hr);
      if (st != null && st.size() != 0) {
        t = hr;
        hr = st.first();
      } else {
        hr = hours.first();
        day++;
      }
      if (hr != t) {
        cl.set(Calendar.SECOND, 0);
        cl.set(Calendar.MINUTE, 0);
        cl.set(Calendar.DAY_OF_MONTH, day);
        setCalendarHour(cl, hr);
        continue;
      }
      cl.set(Calendar.HOUR_OF_DAY, hr);

      day = cl.get(Calendar.DAY_OF_MONTH);
      var mon = cl.get(Calendar.MONTH) + 1;
      // '+ 1' because calendar is 0-based for this field, and we are
      // 1-based
      t = -1;
      var tmon = mon;

      // get day...................................................
      var dayOfMSpec = !daysOfMonth.contains(NO_SPEC);
      var dayOfWSpec = !daysOfWeek.contains(NO_SPEC);
      if (dayOfMSpec && !dayOfWSpec) { // get day by day of month rule
        st = daysOfMonth.tailSet(day);
        if (lastdayOfMonth) {
          if (!nearestWeekday) {
            t = day;
            day = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));
            day -= lastdayOffset;
            if (t > day) {
              mon++;
              if (mon > 12) {
                mon = 1;
                tmon = 3333; // ensure test of mon != tmon further below fails
                cl.add(Calendar.YEAR, 1);
              }
              day = 1;
            }
          } else {
            t = day;
            day = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));
            day -= lastdayOffset;

            var tcal = java.util.Calendar.getInstance(getTimeZone());
            tcal.set(Calendar.SECOND, 0);
            tcal.set(Calendar.MINUTE, 0);
            tcal.set(Calendar.HOUR_OF_DAY, 0);
            tcal.set(Calendar.DAY_OF_MONTH, day);
            tcal.set(Calendar.MONTH, mon - 1);
            tcal.set(Calendar.YEAR, cl.get(Calendar.YEAR));

            var ldom = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));
            var dow = tcal.get(Calendar.DAY_OF_WEEK);

            if (dow == Calendar.SATURDAY && day == 1) {
              day += 2;
            } else if (dow == Calendar.SATURDAY) {
              day -= 1;
            } else if (dow == Calendar.SUNDAY && day == ldom) {
              day -= 2;
            } else if (dow == Calendar.SUNDAY) {
              day += 1;
            }

            tcal.set(Calendar.SECOND, sec);
            tcal.set(Calendar.MINUTE, min);
            tcal.set(Calendar.HOUR_OF_DAY, hr);
            tcal.set(Calendar.DAY_OF_MONTH, day);
            tcal.set(Calendar.MONTH, mon - 1);
            var nTime = tcal.getTime();
            if (nTime.before(afterTime)) {
              day = 1;
              mon++;
            }
          }
        } else if (nearestWeekday) {
          t = day;
          day = daysOfMonth.first();

          var tcal = java.util.Calendar.getInstance(getTimeZone());
          tcal.set(Calendar.SECOND, 0);
          tcal.set(Calendar.MINUTE, 0);
          tcal.set(Calendar.HOUR_OF_DAY, 0);
          tcal.set(Calendar.DAY_OF_MONTH, day);
          tcal.set(Calendar.MONTH, mon - 1);
          tcal.set(Calendar.YEAR, cl.get(Calendar.YEAR));

          var ldom = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));
          var dow = tcal.get(Calendar.DAY_OF_WEEK);

          if (dow == Calendar.SATURDAY && day == 1) {
            day += 2;
          } else if (dow == Calendar.SATURDAY) {
            day -= 1;
          } else if (dow == Calendar.SUNDAY && day == ldom) {
            day -= 2;
          } else if (dow == Calendar.SUNDAY) {
            day += 1;
          }

          tcal.set(Calendar.SECOND, sec);
          tcal.set(Calendar.MINUTE, min);
          tcal.set(Calendar.HOUR_OF_DAY, hr);
          tcal.set(Calendar.DAY_OF_MONTH, day);
          tcal.set(Calendar.MONTH, mon - 1);
          var nTime = tcal.getTime();
          if (nTime.before(afterTime)) {
            day = daysOfMonth.first();
            mon++;
          }
        } else if (st != null && st.size() != 0) {
          t = day;
          day = st.first();
          // make sure we don't over-run a short month, such as february
          var lastDay = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));
          if (day > lastDay) {
            day = daysOfMonth.first();
            mon++;
          }
        } else {
          day = daysOfMonth.first();
          mon++;
        }

        if (day != t || mon != tmon) {
          cl.set(Calendar.SECOND, 0);
          cl.set(Calendar.MINUTE, 0);
          cl.set(Calendar.HOUR_OF_DAY, 0);
          cl.set(Calendar.DAY_OF_MONTH, day);
          cl.set(Calendar.MONTH, mon - 1);
          // '- 1' because calendar is 0-based for this field, and we
          // are 1-based
          continue;
        }
      } else if (dayOfWSpec && !dayOfMSpec) { // get day by day of week rule
        if (lastdayOfWeek) { // are we looking for the last XXX day of
          // the month?
          int dow = daysOfWeek.first(); // desired
          // d-o-w
          var cDow = cl.get(Calendar.DAY_OF_WEEK); // current d-o-w
          var daysToAdd = 0;
          if (cDow < dow) {
            daysToAdd = dow - cDow;
          }
          if (cDow > dow) {
            daysToAdd = dow + (7 - cDow);
          }

          var lDay = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));

          if (day + daysToAdd > lDay) { // did we already miss the
            // last one?
            cl.set(Calendar.SECOND, 0);
            cl.set(Calendar.MINUTE, 0);
            cl.set(Calendar.HOUR_OF_DAY, 0);
            cl.set(Calendar.DAY_OF_MONTH, 1);
            cl.set(Calendar.MONTH, mon);
            // no '- 1' here because we are promoting the month
            continue;
          }

          // find date of last occurrence of this day in this month...
          while ((day + daysToAdd + 7) <= lDay) {
            daysToAdd += 7;
          }

          day += daysToAdd;

          if (daysToAdd > 0) {
            cl.set(Calendar.SECOND, 0);
            cl.set(Calendar.MINUTE, 0);
            cl.set(Calendar.HOUR_OF_DAY, 0);
            cl.set(Calendar.DAY_OF_MONTH, day);
            cl.set(Calendar.MONTH, mon - 1);
            // '- 1' here because we are not promoting the month
            continue;
          }

        } else if (nthdayOfWeek != 0) {
          // are we looking for the Nth XXX day in the month?
          int dow = daysOfWeek.first(); // desired
          // d-o-w
          var cDow = cl.get(Calendar.DAY_OF_WEEK); // current d-o-w
          var daysToAdd = 0;
          if (cDow < dow) {
            daysToAdd = dow - cDow;
          } else if (cDow > dow) {
            daysToAdd = dow + (7 - cDow);
          }

          var dayShifted = daysToAdd > 0;

          day += daysToAdd;
          var weekOfMonth = day / 7;
          if (day % 7 > 0) {
            weekOfMonth++;
          }

          daysToAdd = (nthdayOfWeek - weekOfMonth) * 7;
          day += daysToAdd;
          if (daysToAdd < 0 || day > getLastDayOfMonth(mon, cl.get(Calendar.YEAR))) {
            cl.set(Calendar.SECOND, 0);
            cl.set(Calendar.MINUTE, 0);
            cl.set(Calendar.HOUR_OF_DAY, 0);
            cl.set(Calendar.DAY_OF_MONTH, 1);
            cl.set(Calendar.MONTH, mon);
            // no '- 1' here because we are promoting the month
            continue;
          } else if (daysToAdd > 0 || dayShifted) {
            cl.set(Calendar.SECOND, 0);
            cl.set(Calendar.MINUTE, 0);
            cl.set(Calendar.HOUR_OF_DAY, 0);
            cl.set(Calendar.DAY_OF_MONTH, day);
            cl.set(Calendar.MONTH, mon - 1);
            // '- 1' here because we are NOT promoting the month
            continue;
          }
        } else {
          var cDow = cl.get(Calendar.DAY_OF_WEEK); // current d-o-w
          int dow = daysOfWeek.first(); // desired
          // d-o-w
          st = daysOfWeek.tailSet(cDow);
          if (st != null && st.size() > 0) {
            dow = st.first();
          }

          var daysToAdd = 0;
          if (cDow < dow) {
            daysToAdd = dow - cDow;
          }
          if (cDow > dow) {
            daysToAdd = dow + (7 - cDow);
          }

          var lDay = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));

          if (day + daysToAdd > lDay) { // will we pass the end of
            // the month?
            cl.set(Calendar.SECOND, 0);
            cl.set(Calendar.MINUTE, 0);
            cl.set(Calendar.HOUR_OF_DAY, 0);
            cl.set(Calendar.DAY_OF_MONTH, 1);
            cl.set(Calendar.MONTH, mon);
            // no '- 1' here because we are promoting the month
            continue;
          } else if (daysToAdd > 0) { // are we swithing days?
            cl.set(Calendar.SECOND, 0);
            cl.set(Calendar.MINUTE, 0);
            cl.set(Calendar.HOUR_OF_DAY, 0);
            cl.set(Calendar.DAY_OF_MONTH, day + daysToAdd);
            cl.set(Calendar.MONTH, mon - 1);
            // '- 1' because calendar is 0-based for this field,
            // and we are 1-based
            continue;
          }
        }
      } else { // dayOfWSpec && !dayOfMSpec
        throw new UnsupportedOperationException(
            "Support for specifying both a day-of-week AND a day-of-month parameter is not"
                + " implemented.");
      }
      cl.set(Calendar.DAY_OF_MONTH, day);

      mon = cl.get(Calendar.MONTH) + 1;
      // '+ 1' because calendar is 0-based for this field, and we are
      // 1-based
      var year = cl.get(Calendar.YEAR);
      t = -1;

      // test for expressions that never generate a valid fire date,
      // but keep looping...
      if (year > MAX_YEAR) {
        return null;
      }

      // get month...................................................
      st = months.tailSet(mon);
      if (st != null && st.size() != 0) {
        t = mon;
        mon = st.first();
      } else {
        mon = months.first();
        year++;
      }
      if (mon != t) {
        cl.set(Calendar.SECOND, 0);
        cl.set(Calendar.MINUTE, 0);
        cl.set(Calendar.HOUR_OF_DAY, 0);
        cl.set(Calendar.DAY_OF_MONTH, 1);
        cl.set(Calendar.MONTH, mon - 1);
        // '- 1' because calendar is 0-based for this field, and we are
        // 1-based
        cl.set(Calendar.YEAR, year);
        continue;
      }
      cl.set(Calendar.MONTH, mon - 1);
      // '- 1' because calendar is 0-based for this field, and we are
      // 1-based

      year = cl.get(Calendar.YEAR);
      t = -1;

      // get year...................................................
      st = years.tailSet(year);
      if (st != null && st.size() != 0) {
        t = year;
        year = st.first();
      } else {
        return null; // ran out of years...
      }

      if (year != t) {
        cl.set(Calendar.SECOND, 0);
        cl.set(Calendar.MINUTE, 0);
        cl.set(Calendar.HOUR_OF_DAY, 0);
        cl.set(Calendar.DAY_OF_MONTH, 1);
        cl.set(Calendar.MONTH, 0);
        // '- 1' because calendar is 0-based for this field, and we are
        // 1-based
        cl.set(Calendar.YEAR, year);
        continue;
      }
      cl.set(Calendar.YEAR, year);

      gotOne = true;
    } // while( !done )

    return cl.getTime();
  }