public TimeInstant cloneInstance() {
   TimeInstant clone = new TimeInstant();
   clone.timeMode = timeMode;
   clone.intervalType = intervalType;
   clone.firstMonthOfYear = firstMonthOfYear;
   if (timeAmount != null) clone.timeAmount = timeAmount.cloneInstance();
   return clone;
 }
  /**
   * Parses a time instant expression.
   *
   * @param timeInstantExpr A valid time instant expression (<i>see TimeInstant class javadoc</i>)
   * @return A TimeInstant instance
   * @throws IllegalArgumentException If the expression is not valid
   */
  public static TimeInstant parse(String timeInstantExpr) {
    if (timeInstantExpr == null || timeInstantExpr.length() == 0) {
      throw new IllegalArgumentException("Empty time instant expression");
    }
    TimeInstant instant = new TimeInstant();
    String expr = timeInstantExpr.toLowerCase().trim();

    // now + time amount (optional)
    boolean begin = expr.startsWith("begin");
    boolean end = expr.startsWith("end");
    if (!begin && !end) {
      if (expr.startsWith("now")) {
        instant.setTimeMode(TimeMode.NOW);
        if (expr.length() > 3) {
          instant.setTimeAmount(TimeAmount.parse(expr.substring(3)));
        }
      } else {
        instant.setTimeMode(null);
        instant.setTimeAmount(TimeAmount.parse(expr));
      }
      return instant;
    }
    // begin/end modes
    instant.setTimeMode(begin ? TimeMode.BEGIN : TimeMode.END);

    // Look for braces limits "begin[year March]"
    String example = begin ? "begin[year March]" : "end[year March]";
    int bracesBegin = expr.indexOf('[');
    int bracesEnd = expr.indexOf(']');
    if (bracesBegin == -1 || bracesEnd == -1 || bracesBegin >= bracesEnd) {
      throw new IllegalArgumentException(
          "Missing braces (ex '" + example + "'):  " + timeInstantExpr);
    }
    // Interval type
    String[] intervalTerms = expr.substring(bracesBegin + 1, bracesEnd).split("\\s+");
    if (intervalTerms.length > 2) {
      throw new IllegalArgumentException(
          "Too many settings (ex '" + example + "'):  " + timeInstantExpr);
    }
    instant.setIntervalType(DateIntervalType.getByName(intervalTerms[0]));
    if (instant.getIntervalType() == null) {
      throw new IllegalArgumentException(
          "Invalid interval (ex '" + example + "'): " + timeInstantExpr);
    }

    // First month of year
    if (intervalTerms.length == 2) {
      instant.setFirstMonthOfYear(Month.getByName(intervalTerms[1]));
      if (instant.getFirstMonthOfYear() == null) {
        throw new IllegalArgumentException(
            "Invalid first year month (ex '" + example + "'): " + timeInstantExpr);
      }
    }
    // Time amount
    if (bracesEnd < expr.length()) {
      expr = expr.substring(bracesEnd + 1).trim();
      if (!expr.isEmpty()) {
        TimeAmount timeAmount = TimeAmount.parse(expr);
        instant.setTimeAmount(timeAmount);
      }
    }
    return instant;
  }