package com.hubspot.singularity.helpers;

import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.dmfs.rfc5545.recur.InvalidRecurrenceRuleException;
import org.dmfs.rfc5545.recur.RecurrenceRule;
import org.dmfs.rfc5545.DateTime;
import org.dmfs.rfc5545.recur.RecurrenceRule.Part;
import org.dmfs.rfc5545.recur.RecurrenceRuleIterator;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class RFC5545Schedule {
  public static final int MAX_ITERATIONS = 10000;
  private final RecurrenceRule recurrenceRule;
  private final org.joda.time.DateTime dtStart;

  public RFC5545Schedule(String schedule) throws InvalidRecurrenceRuleException {
    // DTSTART is RFC5545 but NOT in the recur string, but its a nice to have? :)
    Pattern pattern = Pattern.compile("DTSTART=([0-9]{8}T[0-9]{6})");
    Matcher matcher = pattern.matcher(schedule);

    if (matcher.find()) {
      DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyyMMdd'T'HHmmss");
      this.dtStart = formatter.parseDateTime(matcher.group(1));
      this.recurrenceRule = new RecurrenceRule(matcher.replaceAll("").replace("RRULE:", ""));
    } else {
      this.recurrenceRule = new RecurrenceRule(schedule);
      this.dtStart = org.joda.time.DateTime.now().withSecondOfMinute(0);
    }
  }

  public org.joda.time.DateTime getStartDateTime() {
    return dtStart;
  }

  public Date getNextValidTime() {
    final long now = System.currentTimeMillis();
    DateTime startDateTime = new DateTime(dtStart.getYear(), (dtStart.getMonthOfYear() - 1), dtStart.getDayOfMonth(),
      dtStart.getHourOfDay(), dtStart.getMinuteOfHour(), dtStart.getSecondOfMinute());
    RecurrenceRuleIterator timeIterator = recurrenceRule.iterator(startDateTime);

    int count = 0;
    while (timeIterator.hasNext() && (count < MAX_ITERATIONS || (recurrenceRule.hasPart(Part.COUNT) && count < recurrenceRule.getCount()))) {
      count ++;
      long nextRunAtTimestamp = timeIterator.nextMillis();
      if (nextRunAtTimestamp >= now) {
        return new Date(nextRunAtTimestamp);
      }
    }
    return null;
  }

  public RecurrenceRule getRecurrenceRule() {
    return recurrenceRule;
  }
}

package com.hubspot.singularity.helpers;

import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.dmfs.rfc5545.recur.InvalidRecurrenceRuleException;
import org.dmfs.rfc5545.recur.RecurrenceRule;
import org.dmfs.rfc5545.DateTime;
import org.dmfs.rfc5545.recur.RecurrenceRule.Part;
import org.dmfs.rfc5545.recur.RecurrenceRuleIterator;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class RFC5545Schedule {
  public static final int MAX_ITERATIONS = 100000;
  private final RecurrenceRule recurrenceRule;
  private final org.joda.time.DateTime dtStart;

  public RFC5545Schedule(String schedule) throws InvalidRecurrenceRuleException {
    // DTSTART is RFC5545 but NOT in the recur string, but its a nice to have? :)
    Pattern pattern = Pattern.compile("DTSTART=([0-9]{8}T[0-9]{6})");
    Matcher matcher = pattern.matcher(schedule);

    if (matcher.find()) {
      DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyyMMdd'T'HHmmss");
      this.dtStart = formatter.parseDateTime(matcher.group(1));
      this.recurrenceRule = new RecurrenceRule(matcher.replaceAll("").replace("RRULE:", ""));
    } else {
      this.recurrenceRule = new RecurrenceRule(schedule);
      this.dtStart = org.joda.time.DateTime.now().withSecondOfMinute(0);
    }
  }

  public org.joda.time.DateTime getStartDateTime() {
    return dtStart;
  }

  public Date getNextValidTime() {
    final long now = System.currentTimeMillis();
    DateTime startDateTime = new DateTime(dtStart.getYear(), (dtStart.getMonthOfYear() - 1), dtStart.getDayOfMonth(),
      dtStart.getHourOfDay(), dtStart.getMinuteOfHour(), dtStart.getSecondOfMinute());
    RecurrenceRuleIterator timeIterator = recurrenceRule.iterator(startDateTime);

    int count = 0;
    while (timeIterator.hasNext() && (count < MAX_ITERATIONS || (recurrenceRule.hasPart(Part.COUNT) && count < recurrenceRule.getCount()))) {
      count ++;
      long nextRunAtTimestamp = timeIterator.nextMillis();
      if (nextRunAtTimestamp >= now) {
        return new Date(nextRunAtTimestamp);
      }
    }
    return null;
  }

  public RecurrenceRule getRecurrenceRule() {
    return recurrenceRule;
  }
}

increase max iterations