/**
  * Returns true iff this date contains the date represented by other. A range contains a date if
  * it is equal to or after the start date and equal to or before the end date. For open ranges,
  * contains is also inclusive of the one end point.
  */
 public boolean contains(ISODateInstance other) {
   if (this.isUnparseable() || other.isUnparseable()) {
     return this.isoDate.equals(other.isoDate);
   }
   String start = this.getStartDate();
   if (!start.equals("")) { // we have a start date, need to make sure other is after it
     String startOther = other.getStartDate();
     if (startOther.equals("")) {
       return false; // incompatible
     } else {
       if (!isAfter(startOther, start)) {
         return false;
       }
     }
   }
   // now we've found out that the start date is appropriate, check the end date
   String end = this.getEndDate();
   if (!end.equals("")) {
     String endOther = other.getEndDate();
     if (endOther.equals("")) {
       return false;
     } else {
       if (!isAfter(end, endOther)) {
         return false;
       }
     }
   }
   return true; // passes both start and end
 }
 public void testDateNormalization() {
   assertEquals(dateStrings.length, dateAnswers.length);
   for (int i = 0; i < dateStrings.length; i++) {
     ISODateInstance d = new ISODateInstance(dateStrings[i]);
     assertEquals("Testing " + dateStrings[i], dateAnswers[i], d.toString());
   }
 }
  /**
   * Looks if the days for the two dates are compatible. This method does not consider ranges and
   * uses only the start date.
   */
  public boolean isDayCompatible(ISODateInstance other) {
    if (this.isUnparseable() || other.isUnparseable()) {
      return this.isoDate.equals(other.isoDate);
    }

    return isDayCompatible(isoDate, other.getDateString());
  }
 /** For testing only */
 public static void main(String[] args) {
   Properties props = StringUtils.argsToProperties(args);
   String dateProperty = props.getProperty("date");
   if (dateProperty != null) {
     ISODateInstance d = new ISODateInstance(dateProperty);
     System.out.println(dateProperty + " processed as " + d.toString());
   }
 }
  public boolean isCompatibleDate(ISODateInstance other) {
    if (this.isUnparseable() || other.isUnparseable()) {
      return this.isoDate.equals(other.isoDate);
    }

    // first see if either is a range
    if (this.isRange()) {
      return this.contains(other);
    } else if (other.isRange()) {
      return false; // not compatible if other is range and this isn't
    } else {
      return isCompatible(isoDate, other.getDateString());
    }
  }
 private void incrementMonth(ISODateInstance referenceDate, Pair<DateField, Integer> relation) {
   String origDateString = referenceDate.getStartDate();
   String monthString = origDateString.substring(4, 6);
   if (monthString.contains("*")) {
     isoDate = origDateString;
     return;
   }
   // Month is not a variable
   Integer monthNum = Integer.parseInt(monthString);
   // Check if we're an edge case
   if (((monthNum + relation.second()) > 12) || ((monthNum + relation.second) < 1)) {
     boolean decreasing = ((monthNum + relation.second) < 1);
     int newMonthNum = (monthNum + relation.second()) % 12;
     if (newMonthNum < 0) {
       newMonthNum *= -1;
     }
     // Set the month appropriately
     isoDate = makeStringMonthChange(origDateString, newMonthNum);
     // Increment the year if possible
     String yearString = origDateString.substring(0, 4);
     if (!yearString.contains("*")) {
       // How much we increment depends on above mod
       int numYearsToIncrement = (int) Math.ceil(relation.second() / 12.0);
       if (decreasing) {
         isoDate =
             makeStringYearChange(isoDate, Integer.parseInt(yearString) - numYearsToIncrement);
       } else {
         isoDate =
             makeStringYearChange(isoDate, Integer.parseInt(yearString) + numYearsToIncrement);
       }
     }
   } else {
     isoDate = makeStringMonthChange(origDateString, (monthNum + relation.second()));
   }
 }
 public void testIsCompatible() {
   for (int i = 0; i < staticCompatibleStrings1.length; i++) {
     assertEquals(
         "Testing " + staticCompatibleStrings1[i] + " and " + staticCompatibleStrings2[i],
         staticCompatibleAnswers[i],
         ISODateInstance.isCompatible(staticCompatibleStrings1[i], staticCompatibleStrings2[i]));
   }
 }
 public void testIsAfter() {
   for (int i = 0; i < staticAfterStrings1.length; i++) {
     assertEquals(
         "Testing " + staticAfterStrings1[i] + " and " + staticAfterStrings2[i],
         staticAfterAnswers[i],
         ISODateInstance.isAfter(staticAfterStrings1[i], staticAfterStrings2[i]));
   }
 }
 private void incrementYear(ISODateInstance referenceDate, Pair<DateField, Integer> relation) {
   String origDateString = referenceDate.getStartDate();
   String yearString = origDateString.substring(0, 4);
   if (yearString.contains("*")) {
     isoDate = origDateString;
     return;
   }
   isoDate =
       makeStringYearChange(origDateString, Integer.parseInt(yearString) + relation.second());
 }
  /**
   * Uses regexp matching to match month, day, and year fields TODO: Find a way to mark what;s
   * already been handled in the string
   */
  public boolean extractFields(String inputDate) {

    if (tokens.size() < 2) {
      tokenizeDate(inputDate);
    }
    if (DEBUG) {
      System.err.println("Extracting date: " + inputDate);
    }
    // first we see if it's a hyphen and two parseable dates - if not, we treat it as one date
    Pair<String, String> dateEndpoints = getRangeDates(inputDate);
    if (dateEndpoints != null) {
      ISODateInstance date1 = new ISODateInstance(dateEndpoints.first());
      if (dateEndpoints.first().contains(" ") && !dateEndpoints.second().contains(" ")) {
        // consider whether it's a leading modifier; e.g., "June 8-10" will be split into June 8,
        // and 10 when really we'd like June 8 and June 10
        String date =
            dateEndpoints.first().substring(0, dateEndpoints.first().indexOf(' '))
                + ' '
                + dateEndpoints.second();
        ISODateInstance date2 = new ISODateInstance(date);
        if (!date1.isUnparseable() && !date2.isUnparseable()) {
          isoDate = (new ISODateInstance(date1, date2)).getDateString();
          return true;
        }
      }

      ISODateInstance date2 = new ISODateInstance(dateEndpoints.second());
      if (!date1.isUnparseable() && !date2.isUnparseable()) {
        isoDate = (new ISODateInstance(date1, date2)).getDateString();
        return true;
      }
    }

    if (extractYYYYMMDD(inputDate)) {
      return true;
    }
    if (extractMMDDYY(inputDate)) {
      return true;
    }
    boolean passed = false;
    passed = extractYear(inputDate) || passed;
    passed = extractMonth(inputDate) || passed;
    passed = extractDay(inputDate) || passed;

    // slightly hacky, but check for some common modifiers that get grouped into the date
    passed = addExtraRanges(inputDate) || passed;

    if (!passed) { // couldn't parse
      // try one more trick
      unparseable = true;
      boolean weekday = extractWeekday(inputDate);
      if (!weekday) {
        isoDate = inputDate;
      }
    }
    return passed;
  }
  /** Constructor for a range of dates, beginning at date start and finishing at date end */
  public ISODateInstance(ISODateInstance start, ISODateInstance end) {
    String startString = start.getDateString();
    if (start.isRange()) {
      startString = start.getStartDate();
    }
    String endString = end.getDateString();
    if (end.isRange()) {
      endString = end.getEndDate();
    }

    isoDate = startString + '/' + endString;
    unparseable = (start.isUnparseable() || end.isUnparseable());
  }
 /**
  * Takes a string already formatted in ISODateInstance format (such as one previously written out
  * using toString) and creates a new date instance from it
  */
 public static ISODateInstance fromDateString(String date) {
   ISODateInstance d = new ISODateInstance();
   d.isoDate = date;
   return d;
 }
  private void incrementDay(ISODateInstance referenceDate, Pair<DateField, Integer> relation) {
    String origDateString = referenceDate.getStartDate();
    String dayString =
        origDateString.substring(origDateString.length() - 2, origDateString.length());
    if (dayString.contains("*")) {
      isoDate = origDateString;
      return;
    }
    // Date is not a variable
    Integer dayNum = Integer.parseInt(dayString);
    String monthString =
        origDateString.substring(origDateString.length() - 4, origDateString.length() - 2);
    int numDaysInMonth = 30; // default - assume this if month is a variable
    int monthNum =
        -1; // ie, we don't know the month yet - this remains -1 if the month is a variable
    if (!monthString.contains("*")) {
      // Set appropriate numDaysInMonth and monthNum
      monthNum = Integer.parseInt(monthString);
      numDaysInMonth = daysPerMonth.get(monthNum);
    }

    // Now, find out if we're an edge case (potential to increment month)
    if (dayNum + relation.second() <= numDaysInMonth && dayNum + relation.second() >= 1) {
      // Not an edge case - just increment the day, create a new string, and return
      dayNum += relation.second();
      isoDate = makeStringDayChange(origDateString, dayNum);
      return;
    }

    // Since we're an edge case, the month can't be a variable - if it is a variable, just set this
    // to the reference string
    if (monthNum == -1) {
      isoDate = origDateString;
      return;
    }
    // At this point, neither our day nor our month is a variable
    isoDate = origDateString;
    boolean decreasing = (dayNum + relation.second() < 1);
    // Need to increment the month, set the date appropriately - we need the new month num to set
    // the day appropriately, so do month first
    int newMonthNum;
    // Now, check if we're an edge case for month
    if ((monthNum + 1 > 12 && !decreasing) || (monthNum - 1 < 1 && decreasing)) {
      // First, change the month
      if (decreasing) {
        newMonthNum = 12;
      } else {
        newMonthNum = 1;
      }
      // If we can, increment the year
      // TODO: fix this to work more nicely with variables and thus handle more cases
      String yearString = origDateString.substring(0, 4);
      if (!yearString.contains("*")) {
        if (decreasing) {
          isoDate = makeStringYearChange(isoDate, Integer.parseInt(yearString) - 1);
        } else {
          isoDate = makeStringYearChange(isoDate, Integer.parseInt(yearString) + 1);
        }
      }
    } else {
      // We're not an edge case for month - just increment
      if (decreasing) {
        newMonthNum = monthNum - 1;
      } else {
        newMonthNum = monthNum + 1;
      }
    }
    // do the increment
    isoDate = makeStringMonthChange(isoDate, newMonthNum);
    int newDateNum;
    if (decreasing) {
      newDateNum = -relation.second() + daysPerMonth.get(newMonthNum) - dayNum;
    } else {
      newDateNum = relation.second() - dayNum + daysPerMonth.get(monthNum);
    }
    // Now, change the day in our original string to be appropriate
    isoDate = makeStringDayChange(isoDate, newDateNum);
  }