@Test
  public void testMultiParsers() {
    DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
    DateTimeParser[] parsers = new DateTimeParser[3];
    parsers[0] = DateTimeFormat.forPattern("MM/dd/yyyy").withZone(DateTimeZone.UTC).getParser();
    parsers[1] = DateTimeFormat.forPattern("MM-dd-yyyy").withZone(DateTimeZone.UTC).getParser();
    parsers[2] =
        DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").withZone(DateTimeZone.UTC).getParser();
    builder.append(
        DateTimeFormat.forPattern("MM/dd/yyyy").withZone(DateTimeZone.UTC).getPrinter(), parsers);

    DateTimeFormatter formatter = builder.toFormatter();

    formatter.parseMillis("2009-11-15 14:12:12");
  }
  /**
   * Select a format from a custom pattern.
   *
   * @param pattern pattern specification
   * @throws IllegalArgumentException if the pattern is invalid
   * @see #appendPatternTo
   */
  private static DateTimeFormatter createFormatterForPattern(String pattern) {
    if (pattern == null || pattern.length() == 0) {
      throw new IllegalArgumentException("Invalid pattern specification");
    }
    DateTimeFormatter formatter = null;
    synchronized (cPatternedCache) {
      formatter = cPatternedCache.get(pattern);
      if (formatter == null) {
        DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
        parsePatternTo(builder, pattern);
        formatter = builder.toFormatter();

        cPatternedCache.put(pattern, formatter);
      }
    }
    return formatter;
  }
  /**
   * Parses the given pattern and appends the rules to the given DateTimeFormatterBuilder.
   *
   * @param pattern pattern specification
   * @throws IllegalArgumentException if the pattern is invalid
   * @see #forPattern
   */
  private static void parsePatternTo(DateTimeFormatterBuilder builder, String pattern) {
    int length = pattern.length();
    int[] indexRef = new int[1];

    for (int i = 0; i < length; i++) {
      indexRef[0] = i;
      String token = parseToken(pattern, indexRef);
      i = indexRef[0];

      int tokenLen = token.length();
      if (tokenLen == 0) {
        break;
      }
      char c = token.charAt(0);

      switch (c) {
        case 'G': // era designator (text)
          builder.appendEraText();
          break;
        case 'C': // century of era (number)
          builder.appendCenturyOfEra(tokenLen, tokenLen);
          break;
        case 'x': // weekyear (number)
        case 'y': // year (number)
        case 'Y': // year of era (number)
          if (tokenLen == 2) {
            boolean lenientParse = true;

            // Peek ahead to next token.
            if (i + 1 < length) {
              indexRef[0]++;
              if (isNumericToken(parseToken(pattern, indexRef))) {
                // If next token is a number, cannot support
                // lenient parse, because it will consume digits
                // that it should not.
                lenientParse = false;
              }
              indexRef[0]--;
            }

            // Use pivots which are compatible with SimpleDateFormat.
            switch (c) {
              case 'x':
                builder.appendTwoDigitWeekyear(new DateTime().getWeekyear() - 30, lenientParse);
                break;
              case 'y':
              case 'Y':
              default:
                builder.appendTwoDigitYear(new DateTime().getYear() - 30, lenientParse);
                break;
            }
          } else {
            // Try to support long year values.
            int maxDigits = 9;

            // Peek ahead to next token.
            if (i + 1 < length) {
              indexRef[0]++;
              if (isNumericToken(parseToken(pattern, indexRef))) {
                // If next token is a number, cannot support long years.
                maxDigits = tokenLen;
              }
              indexRef[0]--;
            }

            switch (c) {
              case 'x':
                builder.appendWeekyear(tokenLen, maxDigits);
                break;
              case 'y':
                builder.appendYear(tokenLen, maxDigits);
                break;
              case 'Y':
                builder.appendYearOfEra(tokenLen, maxDigits);
                break;
            }
          }
          break;
        case 'M': // month of year (text and number)
          if (tokenLen >= 3) {
            if (tokenLen >= 4) {
              builder.appendMonthOfYearText();
            } else {
              builder.appendMonthOfYearShortText();
            }
          } else {
            builder.appendMonthOfYear(tokenLen);
          }
          break;
        case 'd': // day of month (number)
          builder.appendDayOfMonth(tokenLen);
          break;
        case 'a': // am/pm marker (text)
          builder.appendHalfdayOfDayText();
          break;
        case 'h': // clockhour of halfday (number, 1..12)
          builder.appendClockhourOfHalfday(tokenLen);
          break;
        case 'H': // hour of day (number, 0..23)
          builder.appendHourOfDay(tokenLen);
          break;
        case 'k': // clockhour of day (1..24)
          builder.appendClockhourOfDay(tokenLen);
          break;
        case 'K': // hour of halfday (0..11)
          builder.appendHourOfHalfday(tokenLen);
          break;
        case 'm': // minute of hour (number)
          builder.appendMinuteOfHour(tokenLen);
          break;
        case 's': // second of minute (number)
          builder.appendSecondOfMinute(tokenLen);
          break;
        case 'S': // fraction of second (number)
          builder.appendFractionOfSecond(tokenLen, tokenLen);
          break;
        case 'e': // day of week (number)
          builder.appendDayOfWeek(tokenLen);
          break;
        case 'E': // dayOfWeek (text)
          if (tokenLen >= 4) {
            builder.appendDayOfWeekText();
          } else {
            builder.appendDayOfWeekShortText();
          }
          break;
        case 'D': // day of year (number)
          builder.appendDayOfYear(tokenLen);
          break;
        case 'w': // week of weekyear (number)
          builder.appendWeekOfWeekyear(tokenLen);
          break;
        case 'z': // time zone (text)
          if (tokenLen >= 4) {
            builder.appendTimeZoneName();
          } else {
            builder.appendTimeZoneShortName();
          }
          break;
        case 'Z': // time zone offset
          if (tokenLen == 1) {
            builder.appendTimeZoneOffset(null, "Z", false, 2, 2);
          } else if (tokenLen == 2) {
            builder.appendTimeZoneOffset(null, "Z", true, 2, 2);
          } else {
            builder.appendTimeZoneId();
          }
          break;
        case '\'': // literal text
          String sub = token.substring(1);
          if (sub.length() == 1) {
            builder.appendLiteral(sub.charAt(0));
          } else {
            // Create copy of sub since otherwise the temporary quoted
            // string would still be referenced internally.
            builder.appendLiteral(new String(sub));
          }
          break;
        default:
          throw new IllegalArgumentException("Illegal pattern component: " + token);
      }
    }
  }