public static ZonedDateTime[] getAdjustedMaturityDateSchedule(
      final ZonedDateTime effectiveDate,
      final ZonedDateTime[] dates,
      final BusinessDayConvention convention,
      final Calendar calendar,
      final Frequency frequency) {
    ArgumentChecker.notEmpty(dates, "dates");
    ArgumentChecker.notNull(convention, "convention");
    ArgumentChecker.notNull(calendar, "calendar");
    ArgumentChecker.notNull(frequency, "frequency");

    PeriodFrequency periodFrequency;
    if (frequency instanceof PeriodFrequency) {
      periodFrequency = (PeriodFrequency) frequency;
    } else if (frequency instanceof SimpleFrequency) {
      periodFrequency = ((SimpleFrequency) frequency).toPeriodFrequency();
    } else {
      throw new IllegalArgumentException(
          "For the moment can only deal with PeriodFrequency and SimpleFrequency");
    }
    final Period period = periodFrequency.getPeriod();

    final int n = dates.length;
    final ZonedDateTime[] results = new ZonedDateTime[n];
    results[0] = effectiveDate.plus(period);
    for (int i = 1; i < n; i++) {
      results[i] =
          convention.adjustDate(
              calendar,
              dates[i - 1].plus(period)); // TODO need to further shift these dates by a convention
    }

    return results;
  }
  /**
   * Counts back from maturityDate, filling to equally spaced dates frequency times a year until the
   * last date <b>after</b> effective date.
   *
   * @param effectiveDate the date that terminates to back counting (i.e. the first date is after
   *     this date), not null
   * @param maturityDate the date to count back from, not null
   * @param frequency how many times a year dates occur, not null
   * @return the first date after effectiveDate (i.e. effectiveDate is <b>not</b> included to the
   *     maturityDate (included)
   */
  public static ZonedDateTime[] getBackwardsUnadjustedDateSchedule(
      final ZonedDateTime effectiveDate,
      final ZonedDateTime maturityDate,
      final Frequency frequency) {
    ArgumentChecker.notNull(effectiveDate, "effective date");
    ArgumentChecker.notNull(maturityDate, "maturity date");
    ArgumentChecker.notNull(frequency, "frequency");
    if (effectiveDate.isAfter(maturityDate)) {
      throw new IllegalArgumentException("Effective date was after maturity");
    }

    PeriodFrequency periodFrequency;
    if (frequency instanceof PeriodFrequency) {
      periodFrequency = (PeriodFrequency) frequency;
    } else if (frequency instanceof SimpleFrequency) {
      periodFrequency = ((SimpleFrequency) frequency).toPeriodFrequency();
    } else {
      throw new IllegalArgumentException(
          "For the moment can only deal with PeriodFrequency and SimpleFrequency");
    }
    final Period period = periodFrequency.getPeriod();
    final List<ZonedDateTime> dates = new ArrayList<>();
    ZonedDateTime date = maturityDate;

    // TODO review the tolerance given
    while (date.isAfter(effectiveDate)
        && DateUtils.getExactDaysBetween(effectiveDate, date) > 4.0) {
      dates.add(date);
      date = date.minus(period);
    }

    Collections.sort(dates);
    return dates.toArray(EMPTY_ARRAY);
  }
 /**
  * Convert a Frequency to a Period when possible.
  *
  * @param frequency The frequency.
  * @return The converted period.
  */
 private static Period periodFromFrequency(final Frequency frequency) {
   PeriodFrequency periodFrequency;
   if (frequency instanceof PeriodFrequency) {
     periodFrequency = (PeriodFrequency) frequency;
   } else if (frequency instanceof SimpleFrequency) {
     periodFrequency = ((SimpleFrequency) frequency).toPeriodFrequency();
   } else {
     throw new IllegalArgumentException(
         "For the moment can only deal with PeriodFrequency and SimpleFrequency");
   }
   return periodFrequency.getPeriod();
 }
 public static ZonedDateTime[] getAdjustedDateSchedule(
     final ZonedDateTime startDate,
     final ZonedDateTime endDate,
     final Frequency frequency,
     final BusinessDayConvention businessDayConvention,
     final Calendar calendar,
     final boolean isEOM) {
   PeriodFrequency periodFrequency;
   if (frequency instanceof PeriodFrequency) {
     periodFrequency = (PeriodFrequency) frequency;
   } else if (frequency instanceof SimpleFrequency) {
     periodFrequency = ((SimpleFrequency) frequency).toPeriodFrequency();
   } else {
     throw new IllegalArgumentException(
         "For the moment can only deal with PeriodFrequency and SimpleFrequency");
   }
   final Period period = periodFrequency.getPeriod();
   return getAdjustedDateSchedule(
       startDate, endDate, period, businessDayConvention, calendar, isEOM, true);
 }
  /**
   * Calculates the unadjusted date schedule.
   *
   * @param effectiveDate the effective date, not null
   * @param accrualDate the accrual date, not null
   * @param maturityDate the maturity date, not null
   * @param frequency how many times a year dates occur, not null
   * @return the schedule, not null
   */
  public static ZonedDateTime[] getUnadjustedDateSchedule(
      final ZonedDateTime effectiveDate,
      final ZonedDateTime accrualDate,
      final ZonedDateTime maturityDate,
      final Frequency frequency) {
    ArgumentChecker.notNull(effectiveDate, "effective date");
    ArgumentChecker.notNull(accrualDate, "accrual date");
    ArgumentChecker.notNull(maturityDate, "maturity date");
    ArgumentChecker.notNull(frequency, "frequency");
    if (effectiveDate.isAfter(maturityDate)) {
      throw new IllegalArgumentException("Effective date was after maturity");
    }
    if (accrualDate.isAfter(maturityDate)) {
      throw new IllegalArgumentException("Accrual date was after maturity");
    }

    // TODO what if there's no valid date between accrual date and maturity date?
    PeriodFrequency periodFrequency;
    if (frequency instanceof PeriodFrequency) {
      periodFrequency = (PeriodFrequency) frequency;
    } else if (frequency instanceof SimpleFrequency) {
      periodFrequency = ((SimpleFrequency) frequency).toPeriodFrequency();
    } else {
      throw new IllegalArgumentException(
          "For the moment can only deal with PeriodFrequency and SimpleFrequency");
    }
    final Period period = periodFrequency.getPeriod();
    final List<ZonedDateTime> dates = new ArrayList<>();
    ZonedDateTime date =
        effectiveDate; // TODO this is only correct if effective date = accrual date
    date = date.plus(period);
    while (isWithinSwapLifetime(
        date,
        maturityDate)) { // REVIEW: could speed this up by working out how many periods between
      // start and end date?
      dates.add(date);
      date = date.plus(period);
    }
    return dates.toArray(EMPTY_ARRAY);
  }