/**
   * Creates a new date by adding the specified number of months to the base date.
   *
   * <p>If the base date is close to the end of the month, the day on the result may be adjusted
   * slightly: 31 May + 1 month = 30 June.
   *
   * @param months the number of months to add (can be negative).
   * @param base the base date.
   * @return a new date.
   */
  public static SerialDate addMonths(final int months, final SerialDate base) {

    final int yy = (12 * base.getYYYY() + base.getMonth() + months - 1) / 12;
    final int mm = (12 * base.getYYYY() + base.getMonth() + months - 1) % 12 + 1;
    final int dd = Math.min(base.getDayOfMonth(), SerialDate.lastDayOfMonth(mm, yy));
    return SerialDate.createInstance(dd, mm, yy);
  }
  /**
   * Creates a new date by adding the specified number of years to the base date.
   *
   * @param years the number of years to add (can be negative).
   * @param base the base date.
   * @return A new date.
   */
  public static SerialDate addYears(final int years, final SerialDate base) {

    final int baseY = base.getYYYY();
    final int baseM = base.getMonth();
    final int baseD = base.getDayOfMonth();

    final int targetY = baseY + years;
    final int targetD = Math.min(baseD, SerialDate.lastDayOfMonth(baseM, targetY));

    return SerialDate.createInstance(targetD, baseM, targetY);
  }
  /**
   * Returns the date that falls on the specified day-of-the-week and is CLOSEST to the base date.
   *
   * @param targetDOW a code for the target day-of-the-week.
   * @param base the base date.
   * @return the date that falls on the specified day-of-the-week and is CLOSEST to the base date.
   */
  public static SerialDate getNearestDayOfWeek(final int targetDOW, final SerialDate base) {

    // check arguments...
    if (!SerialDate.isValidWeekdayCode(targetDOW)) {
      throw new IllegalArgumentException("Invalid day-of-the-week code.");
    }

    // find the date...
    final int baseDOW = base.getDayOfWeek();
    int adjust = -Math.abs(targetDOW - baseDOW);
    if (adjust >= 4) {
      adjust = 7 - adjust;
    }
    if (adjust <= -4) {
      adjust = 7 + adjust;
    }
    return SerialDate.addDays(adjust, base);
  }
  /**
   * Returns the earliest date that falls on the specified day-of-the-week and is AFTER the base
   * date.
   *
   * @param targetWeekday a code for the target day-of-the-week.
   * @param base the base date.
   * @return the earliest date that falls on the specified day-of-the-week and is AFTER the base
   *     date.
   */
  public static SerialDate getFollowingDayOfWeek(final int targetWeekday, final SerialDate base) {

    // check arguments...
    if (!SerialDate.isValidWeekdayCode(targetWeekday)) {
      throw new IllegalArgumentException("Invalid day-of-the-week code.");
    }

    // find the date...
    final int adjust;
    final int baseDOW = base.getDayOfWeek();
    if (baseDOW > targetWeekday) {
      adjust = 7 + Math.min(0, targetWeekday - baseDOW);
    } else {
      adjust = Math.max(0, targetWeekday - baseDOW);
    }

    return SerialDate.addDays(adjust, base);
  }
 /**
  * Converts the date to a string.
  *
  * @return a string representation of the date.
  */
 @Override
 public String toString() {
   return getDayOfMonth() + "-" + SerialDate.monthCodeToString(getMonth()) + "-" + getYYYY();
 }
 /**
  * Rolls the date forward to the last day of the month.
  *
  * @param base the base date.
  * @return a new serial date.
  */
 public SerialDate getEndOfCurrentMonth(final SerialDate base) {
   final int last = SerialDate.lastDayOfMonth(base.getMonth(), base.getYYYY());
   return SerialDate.createInstance(last, base.getMonth(), base.getYYYY());
 }
  /**
   * Creates a new date by adding the specified number of days to the base date.
   *
   * @param days the number of days to add (can be negative).
   * @param base the base date.
   * @return a new date.
   */
  public static SerialDate addDays(final int days, final SerialDate base) {

    final int serialDayNumber = base.toSerial() + days;
    return SerialDate.createInstance(serialDayNumber);
  }