/**
  * Parses the text to a builder.
  *
  * <p>This parses to a {@code DateTimeBuilder} ensuring that the text is fully parsed. This method
  * throws {@link DateTimeParseException} if unable to parse, or some other {@code
  * DateTimeException} if another date/time problem occurs.
  *
  * @param text the text to parse, not null
  * @return the engine representing the result of the parse, not null
  * @throws DateTimeParseException if the parse fails
  * @throws DateTimeException if there is a date/time problem
  */
 public DateTimeBuilder parseToBuilder(CharSequence text) {
   Objects.requireNonNull(text, "text");
   String str = text.toString(); // parsing whole String, so this makes sense
   ParsePosition pos = new ParsePosition(0);
   DateTimeBuilder result = parseToBuilder(str, pos);
   if (result == null || pos.getErrorIndex() >= 0 || pos.getIndex() < str.length()) {
     String abbr = str.toString();
     if (abbr.length() > 64) {
       abbr = abbr.substring(0, 64) + "...";
     }
     if (pos.getErrorIndex() >= 0) {
       throw new DateTimeParseException(
           "Text '" + abbr + "' could not be parsed at index " + pos.getErrorIndex(),
           str,
           pos.getErrorIndex());
     } else {
       throw new DateTimeParseException(
           "Text '"
               + abbr
               + "' could not be parsed, unparsed text found at index "
               + pos.getIndex(),
           str,
           pos.getIndex());
     }
   }
   return result;
 }
  @Override
  public Object parseObject(String s, ParsePosition pos) {
    if (pos.getErrorIndex() >= 0)
      throw new IllegalArgumentException(pos + " has en error at " + pos.getErrorIndex());

    boolean success = false;
    Object tmpRes = null;
    final ParsePosition tmpPos = new ParsePosition(pos.getIndex());
    final Iterator<? extends Format> iter = this.formats.iterator();
    while (iter.hasNext() && !success) {
      final Format f = iter.next();
      mutateTo(tmpPos, pos);
      tmpRes = f.parseObject(s, tmpPos);
      success = tmpPos.getIndex() != pos.getIndex() && tmpPos.getErrorIndex() < 0;
    }

    final Object res;
    if (!success) {
      // fail with the same as format()
      res = this.formats.get(this.formatIndex).parseObject(s, pos);
    } else {
      res = tmpRes;
      mutateTo(pos, tmpPos);
    }

    return res;
  }
 /**
  * Looks at the start of a string for a day (defined in the user's locale). If found, returns
  * String array containing the match (1st elem) and the remaining string (2nd elem). Otherwise,
  * returns null.
  *
  * @param input
  * @return
  */
 @Override
 public String[] find(final String input) {
   result = null;
   pos.setIndex(0);
   result = sdf.parse(input, pos);
   if (result == null) return null;
   return new String[] {input.substring(0, pos.getIndex()), input.substring(pos.getIndex())};
 }
 private Number parseNumber(String val, NumberFormat numFormat) throws ParseException {
   final ParsePosition parsePos = new ParsePosition(0);
   final Number num = numFormat.parse(val, parsePos);
   if (parsePos.getIndex() != val.length()) {
     throw new ParseException("illegal number format", parsePos.getIndex());
   }
   return num;
 }
Beispiel #5
0
  public void test(TestHarness harness) {
    ParsePosition pp = new ParsePosition(69);
    harness.check(pp.getIndex(), 69, "getIndex() post-create");

    pp.setIndex(666);
    harness.check(pp.getIndex(), 666, "set/getIndex()");

    harness.check(pp.getErrorIndex(), -1, "getErrorIndex() no error");

    pp.setErrorIndex(65536);
    harness.check(pp.getErrorIndex(), 65536, "set/getErrorIndex()");

    harness.debug(pp.toString());
  }
Beispiel #6
0
 /** Does the parse-work without the mandatory-check */
 private String parse2(String external) throws FmtParseException {
   // trim blanks on both sides
   if (external == null) return "";
   external = external.trim();
   if (external.length() == 0) return "";
   else {
     ParsePosition pp = new ParsePosition(0);
     Date date = null;
     date = dateFormat_.parse(external, pp);
     if (date == null || (pp.getIndex() != external.length() && pp.getIndex() > 0)) {
       throw new FmtParseException("ATSSyntax", getSampleTS());
     }
     return TimeStampUtil.date2Internal(date);
   }
 }
Beispiel #7
0
    public void outputValues(String[] values) {
      if (values.length > 1) {
        writer.print("[");
      }
      for (int i = 0; i < values.length; i++) {
        String value = values[i];
        if (i > 0) {
          writer.print(", ");
        }

        if (value == null || value.length() == 0) {
          writer.print("null");
        } else {
          // Is it a number?
          ParsePosition pos = new ParsePosition(0);
          formatter.parse(value, pos);
          if (value.length() == pos.getIndex()) {
            // It's a number. Remove leading zeros and output
            value = value.replaceFirst("^0+(\\d)", "$1");
            writer.print(value);
          } else {
            // Not a number, escape it
            gson.toJson(value, writer);
          }
        }
      }
      if (values.length > 1) {
        writer.print("]");
      }
    }
  /**
   * Checks if the value can safely be converted to a byte primitive.
   *
   * @param value The value validation is being performed on.
   * @param locale The locale to use to parse the number (system default if null)
   * @return the converted Byte value.
   */
  public static Byte formatByte(String value, Locale locale) {
    Byte result = null;

    if (value != null) {
      NumberFormat formatter = null;
      if (locale != null) {
        formatter = NumberFormat.getNumberInstance(locale);
      } else {
        formatter = NumberFormat.getNumberInstance(Locale.getDefault());
      }
      formatter.setParseIntegerOnly(true);
      ParsePosition pos = new ParsePosition(0);
      Number num = formatter.parse(value, pos);

      // If there was no error      and we used the whole string
      if (pos.getErrorIndex() == -1
          && pos.getIndex() == value.length()
          && num.doubleValue() >= Byte.MIN_VALUE
          && num.doubleValue() <= Byte.MAX_VALUE) {
        result = new Byte(num.byteValue());
      }
    }

    return result;
  }
Beispiel #9
0
  private static Date parseDateWithLeniency(String str, String[] parsePatterns, boolean lenient)
      throws ParseException {
    if ((str == null) || (parsePatterns == null)) {
      throw new IllegalArgumentException("Date and Patterns must not be null");
    }
    SimpleDateFormat parser = new SimpleDateFormat();
    parser.setLenient(lenient);
    ParsePosition pos = new ParsePosition(0);
    for (int i = 0; i < parsePatterns.length; i++) {
      String pattern = parsePatterns[i];
      if (parsePatterns[i].endsWith("ZZ")) {
        pattern = pattern.substring(0, pattern.length() - 1);
      }
      parser.applyPattern(pattern);
      pos.setIndex(0);

      String str2 = str;
      if (parsePatterns[i].endsWith("ZZ")) {
        int signIdx = indexOfSignChars(str2, 0);
        while (signIdx >= 0) {
          str2 = reformatTimezone(str2, signIdx);
          signIdx = indexOfSignChars(str2, ++signIdx);
        }
      }
      Date date = parser.parse(str2, pos);
      if ((date != null) && (pos.getIndex() == str2.length())) {
        return date;
      }
    }
    throw new ParseException("Unable to parse the date: " + str, -1);
  }
Beispiel #10
0
  /**
   * Parses date given in parameter according the ISO-8601 standard. This parameter should follow a
   * syntax defined in the {@link #PATTERNS} array to be validated.
   *
   * @param value The date to parse.
   * @return A date found in the request.
   * @throws ParseException if the string can not be parsed.
   */
  static Object getFuzzyDate(final String value) throws ParseException {
    String computedValue = value;

    // special handling for current keyword (we accept both wms and wcs ways)
    if (computedValue.equalsIgnoreCase("current") || computedValue.equalsIgnoreCase("now")) {
      return null;
    }

    // Accept new "present" keyword, which actually fills in present time as now should have
    if (computedValue.equalsIgnoreCase("present")) {
      Calendar now = Calendar.getInstance();
      now.set(Calendar.MILLISECOND, 0);
      computedValue = FormatAndPrecision.MILLISECOND.getFormat().format(now.getTime());
    }

    for (FormatAndPrecision f : FormatAndPrecision.values()) {
      ParsePosition pos = new ParsePosition(0);
      Date time = f.getFormat().parse(computedValue, pos);
      if (pos.getIndex() == computedValue.length()) {
        DateRange range = f.expand(time);
        if (range.getMinValue().equals(range.getMaxValue())) {
          return range.getMinValue();
        } else {
          return range;
        }
      }
    }

    throw new ParseException("Invalid date: " + value, 0);
  }
  public void testPartialParse() throws Exception {
    java.text.ParsePosition pos = new java.text.ParsePosition(0);
    String timestamp = "2007-08-13T19:51:23Z";
    Date result = df.parse(timestamp + "hello", pos);

    assertEquals(date, result);
    assertEquals(timestamp.length(), pos.getIndex());
  }
Beispiel #12
0
  /**
   * Attempts to pass a HTTP date.
   *
   * @param date The date to parse
   * @return The parsed date, or null if parsing failed
   */
  public static Date parseDate(final String date) {

    /*
       IE9 sends a superflous lenght parameter after date in the
       If-Modified-Since header, which needs to be stripped before
       parsing.

    */

    final int semicolonIndex = date.indexOf(';');
    final String trimmedDate = semicolonIndex >= 0 ? date.substring(0, semicolonIndex) : date;

    ParsePosition pp = new ParsePosition(0);
    SimpleDateFormat dateFormat = RFC1123_PATTERN_FORMAT.get();
    Date val = dateFormat.parse(trimmedDate, pp);
    if (val != null && pp.getIndex() == trimmedDate.length()) {
      return val;
    }

    pp = new ParsePosition(0);
    dateFormat = new SimpleDateFormat(RFC1036_PATTERN, LOCALE_US);
    dateFormat.setTimeZone(GMT_ZONE);
    val = dateFormat.parse(trimmedDate, pp);
    if (val != null && pp.getIndex() == trimmedDate.length()) {
      return val;
    }

    pp = new ParsePosition(0);
    dateFormat = new SimpleDateFormat(ASCITIME_PATTERN, LOCALE_US);
    dateFormat.setTimeZone(GMT_ZONE);
    val = dateFormat.parse(trimmedDate, pp);
    if (val != null && pp.getIndex() == trimmedDate.length()) {
      return val;
    }

    pp = new ParsePosition(0);
    dateFormat = new SimpleDateFormat(OLD_COOKIE_PATTERN, LOCALE_US);
    dateFormat.setTimeZone(GMT_ZONE);
    val = dateFormat.parse(trimmedDate, pp);
    if (val != null && pp.getIndex() == trimmedDate.length()) {
      return val;
    }

    return null;
  }
Beispiel #13
0
  /* (non-Javadoc)
   * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
   */
  public Object parseObject(String durationString, ParsePosition pos) {
    Object result = null;
    if (durationString.length() == 0) return null;

    if (durationString.charAt(pos.getIndex()) == '+') // if string begins with + sign, ignore it
    pos.setIndex(pos.getIndex() + 1);

    Number numberResult = DECIMAL_FORMAT.parse(durationString, pos);
    if (numberResult == null) return null;
    String durationPart = durationString.substring(pos.getIndex());
    durationPart = durationPart.trim();
    Matcher matcher;
    for (int i = 0; i < TYPE_COUNT; i++) { // find hte appropriate units
      matcher = pattern[i].matcher(durationPart);
      if (matcher.matches()) {
        int timeUnit =
            (matcher.group(1) != null)
                ? i
                : TimeUnit
                    .NONE; // first group is units.  If no units, then it will match, but should use
                           // default: NONE
        double value = numberResult.doubleValue();
        if (timeUnit == TimeUnit.PERCENT || timeUnit == TimeUnit.ELAPSED_PERCENT) value /= 100.0;
        if (timeUnit == TimeUnit.NONE && isWork) {
          if (canBeNonTemporal) timeUnit = TimeUnit.NON_TEMPORAL;
          else
            timeUnit =
                ScheduleOption.getInstance()
                    .getWorkUnit(); // use default work unit if work and nothing entered
        }
        long longResult = Duration.getInstance(value, timeUnit);
        if (Duration.millis(longResult) > Duration.MAX_DURATION) // check for too big
        return null;
        if (matcher.group(2).length() != 0) { // second group is estimated
          longResult = Duration.setAsEstimated(longResult, true);
        }

        result = new Duration(longResult);

        return result;
      }
    }

    return null;
  }
  private void validateDate(List<String> dateStrings) {
    SimpleDateFormat formatter = new SimpleDateFormat(this.dateFormat);
    formatter.setLenient(false);

    for (String dateString : dateStrings) {
      try {
        ParsePosition pos = new ParsePosition(0);
        formatter.parse(dateString, pos);

        if (pos.getIndex() != dateString.length()) {
          throw new ParseException("Could not parse date parameter", pos.getIndex());
        }
      } catch (ParseException pe) {
        throw new ParameterValidationException(
            name, i18n.tr("Invalid date string. Expected format: {0}", dateFormat));
      }
    }
  }
  public static boolean isAmount(String value) {
    if (StringUtils.isEmpty(value)) {
      return false;
    }

    ParsePosition pos = new ParsePosition(0);
    new DecimalFormat(Constants.AMOUNT_FORMAT).parse(value, pos);
    return pos.getIndex() == value.length();
  }
 /**
  * Parses a string using {@link SimpleDateFormat} and a given pattern. The entire string must
  * match the pattern specified.
  *
  * @param s string to be parsed
  * @param pattern {@link SimpleDateFormat} pattern
  * @param tz time zone in which to interpret string. Defaults to the Java default time zone
  * @return a Calendar initialized with the parsed value, or null if parsing failed. If returned,
  *     the Calendar is configured to the GMT time zone.
  */
 public static Calendar parseDateFormat(String s, String pattern, TimeZone tz) {
   assert pattern != null;
   ParsePosition pp = new ParsePosition(0);
   Calendar ret = parseDateFormat(s, pattern, tz, pp);
   if (pp.getIndex() != s.length()) {
     // Didn't consume entire string - not good
     return null;
   }
   return ret;
 }
Beispiel #17
0
 public static Date parseDatetime(String str) {
   for (String formatStr : DATETIME_FORMATS) {
     SimpleDateFormat fmt = new SimpleDateFormat(formatStr);
     fmt.setLenient(false);
     ParsePosition pp = new ParsePosition(0);
     Date d = fmt.parse(str, pp);
     if (d != null && pp.getIndex() == str.length()) return d;
   }
   return null;
 }
  /**
   * @tests java.text.MessageFormat#parseObject(java.lang.String, java.text.ParsePosition) Test of
   *     method java.text.MessageFormat#parseObject(java.lang.String, java.text.ParsePosition). Case
   *     1: Parsing of correct data string. Case 2: Parsing of partial correct data string. Case 3:
   *     Try to use argument ParsePosition as null.
   */
  @TestTargetNew(
      level = TestLevel.COMPLETE,
      notes = "",
      method = "parseObject",
      args = {java.lang.String.class, java.text.ParsePosition.class})
  public void test_parseObjectLjava_lang_StringLjavajava_text_ParsePosition() {
    MessageFormat mf = new MessageFormat("{0,number,#.##}, {0,number,#.#}");
    try {
      // case 1: Try to parse correct data string.
      Object[] objs = {new Double(3.1415)};
      String result = mf.format(objs);
      // result now equals "3.14, 3.1"
      Object[] res = null;
      ParsePosition pp = new ParsePosition(0);
      int parseIndex = pp.getIndex();
      res = (Object[]) mf.parseObject(result, pp);
      assertTrue("Parse operation return null", res != null);
      assertTrue("parse operation return array with incorrect length", 1 == res.length);
      assertTrue("ParseIndex is incorrect", pp.getIndex() != parseIndex);
      assertTrue("Result object is incorrect", new Double(3.1).equals(res[0]));

      // case 2: Try to parse partially correct data string.
      pp.setIndex(0);
      char[] cur = result.toCharArray();
      cur[cur.length / 2] = 'Z';
      String partialCorrect = new String(cur);
      res = (Object[]) mf.parseObject(partialCorrect, pp);
      assertTrue("Parse operation return null", res == null);
      assertTrue("ParseIndex is incorrect", pp.getIndex() == 0);
      assertTrue("ParseErrorIndex is incorrect", pp.getErrorIndex() == cur.length / 2);

      // case 3: Try to use argument ParsePosition as null.
      try {
        mf.parseObject(result, null);
        fail("Expected NullPointerException was not thrown");
      } catch (NullPointerException e) {
        // expected
      }
    } catch (Exception e) {
      fail("Unexpected exception " + e.toString());
    }
  }
Beispiel #19
0
  /**
   * Parses the supplied string to see if it looks like a date. If so, returns the same date in a
   * cleaned-up format for the user. Otherwise, returns the supplied string unchanged.
   */
  public static String formatDate(Context context, String string) {
    if (string == null) {
      return null;
    }

    string = string.trim();
    if (string.length() == 0) {
      return string;
    }

    ParsePosition parsePosition = new ParsePosition(0);

    Date date;

    synchronized (NO_YEAR_DATE_FORMAT) {
      date = NO_YEAR_DATE_FORMAT.parse(string, parsePosition);
    }

    if (parsePosition.getIndex() == string.length()) {
      java.text.DateFormat outFormat =
          isMonthBeforeDate(context)
              ? FORMAT_WITHOUT_YEAR_MONTH_FIRST
              : FORMAT_WITHOUT_YEAR_DATE_FIRST;
      synchronized (outFormat) {
        return outFormat.format(date);
      }
    }

    for (int i = 0; i < DATE_FORMATS.length; i++) {
      SimpleDateFormat f = DATE_FORMATS[i];
      synchronized (f) {
        parsePosition.setIndex(0);
        date = f.parse(string, parsePosition);
        if (parsePosition.getIndex() == string.length()) {
          java.text.DateFormat outFormat = DateFormat.getDateFormat(context);
          outFormat.setTimeZone(UTC_TIMEZONE);
          return outFormat.format(date);
        }
      }
    }
    return string;
  }
 @Override
 public Date parse(String i, ParsePosition p) {
   /* delegate to SimpleDateFormat for easy stuff */
   Date d = super.parse(i, p);
   int milliIndex = p.getIndex();
   /* worry about the milliseconds ourselves */
   if (null != d
       && -1 == p.getErrorIndex()
       && milliIndex + 1 < i.length()
       && '.' == i.charAt(milliIndex)) {
     p.setIndex(++milliIndex); // NOTE: ++ to chomp '.'
     Number millis = millisParser.parse(i, p);
     if (-1 == p.getErrorIndex()) {
       int endIndex = p.getIndex();
       d =
           new Date(
               d.getTime()
                   + (long) (millis.doubleValue() * Math.pow(10, (3 - endIndex + milliIndex))));
     }
   }
   return d;
 }
 /**
  * Parses the text to a builder.
  *
  * <p>This parses to a {@code DateTimeBuilder} but does not require the input to be fully parsed.
  *
  * <p>This method does not throw {@link DateTimeParseException}. Instead, errors are returned
  * within the state of the specified parse position. Callers must check for errors before using
  * the context.
  *
  * <p>This method may throw some other {@code DateTimeException} if a date/time problem occurs.
  *
  * @param text the text to parse, not null
  * @param position the position to parse from, updated with length parsed and the index of any
  *     error, not null
  * @return the parsed text, null only if the parse results in an error
  * @throws IndexOutOfBoundsException if the position is invalid
  * @throws DateTimeParseException if the parse fails
  * @throws DateTimeException if there is a date/time problem
  */
 public DateTimeBuilder parseToBuilder(CharSequence text, ParsePosition position) {
   Objects.requireNonNull(text, "text");
   Objects.requireNonNull(position, "position");
   DateTimeParseContext context = new DateTimeParseContext(locale, symbols);
   int pos = position.getIndex();
   pos = printerParser.parse(context, text, pos);
   if (pos < 0) {
     position.setErrorIndex(~pos);
     return null;
   }
   position.setIndex(pos);
   return context.toBuilder();
 }
Beispiel #22
0
 /**
  * Parses the supplied string to see if it looks like a date. If so, returns the date. Otherwise,
  * returns null.
  */
 public static Date parseDate(String string) {
   ParsePosition parsePosition = new ParsePosition(0);
   for (int i = 0; i < DATE_FORMATS.length; i++) {
     SimpleDateFormat f = DATE_FORMATS[i];
     synchronized (f) {
       parsePosition.setIndex(0);
       Date date = f.parse(string, parsePosition);
       if (parsePosition.getIndex() == string.length()) {
         return date;
       }
     }
   }
   return null;
 }
 @Override
 public Number parse(String source, ParsePosition pos) {
   if (source == null) {
     pos.setIndex(1); // otherwise Format thinks parse failed
     return null;
   }
   if (source.trim().equals("")) {
     pos.setIndex(1); // otherwise Format thinks parse failed
     return null;
   }
   Number val = childFormat.parse(source, pos);
   /*
    * The default behaviour of Format objects is to keep parsing as long as
    * they encounter valid data. By for table editing we don't want
    * trailing bad data to be considered a "valid value". So set the index
    * to 0 so that the parse(Object) method knows that we had an error.
    */
   if (pos.getIndex() != source.length()) {
     pos.setErrorIndex(pos.getIndex());
     pos.setIndex(0);
   }
   return val;
 }
Beispiel #24
0
  @Override
  public Object fromString(final String value, final Locale locale) {
    ParsePosition parsePosition = new ParsePosition(0);
    String trimedValue = value.replaceAll(" ", "");
    DecimalFormat formatter = (DecimalFormat) NumberFormat.getNumberInstance(locale);
    formatter.setParseBigDecimal(true);
    Object parsedValue = formatter.parseObject(trimedValue, parsePosition);

    if (parsePosition.getIndex() == trimedValue.length()) {
      return parsedValue;
    }

    return value;
  }
Beispiel #25
0
  protected String isCorrectString(String value) {
    String text = value.trim();
    if (sMinValue.equalsIgnoreCase(text) || sMaxValue.equalsIgnoreCase(text)) return null;

    Number result = null;
    if ((fNumberType == DOUBLE || fNumberType == FLOAT)
        && (text.indexOf('e') != -1 || text.indexOf('E') != -1)) {
      // We have a double/float with an exponent. This is scientific notation. Formatter handles
      // them badly, so use parse instead.
      try {
        if (fNumberType == DOUBLE) {
          result = new Double(Double.parseDouble(text));
        } else {
          result = new Float(Float.parseFloat(text));
        }
      } catch (NumberFormatException e) {
      }
    } else {
      // integral or not scientific notation. Let formatter handle it.
      ParsePosition parsePosition = new ParsePosition(0);
      result = fFormatter.parse(text, parsePosition);
      if (parsePosition.getErrorIndex() != -1 || parsePosition.getIndex() != text.length())
        result = null; // Some error
      // Check for out of bounds with long type
      if (fNumberType == LONG && result instanceof Double) {
        result =
            (result.doubleValue() < 0)
                ? MinmaxValidator.LONG_UNDERFLOW
                : MinmaxValidator.LONG_OVERFLOW;
      }
    }

    if (result != null) {
      // Now see if it is valid for the requested type.
      MinmaxValidator v = sMinMaxValidators[fNumberType];

      // Double/Float are special because the min/MIN are on the absolute value, not signed value.
      if (fNumberType == DOUBLE || fNumberType == FLOAT) {
        double d = result.doubleValue();
        if (d == 0.0 || d == -0.0) return null; // +/- zero are valid values.
        result = new Double(Math.abs(d));
      }
      if (v != null) {
        String e = v.isValid(result);
        if (e == null || e.length() == 0) return null;
        return e; // It didn't fit in a the number type.
      }
    }
    return (fFormatter.isParseIntegerOnly() ? sNotIntegerError : sNotNumberError);
  }
  /**
   * Parse the input using a NumberFormatter. If the number cannot be parsed, the error key
   * <em>number.invalidNumber</em> will be added to the errors.
   */
  protected Number parse(String input, Collection<ValidationError> errors) {
    input = preprocess(input);
    ParsePosition pp = new ParsePosition(0);

    for (NumberFormat format : this.formats) {
      pp.setIndex(0);
      Number number = format.parse(input, pp);
      if (number != null && input.length() == pp.getIndex()) return number;
    }

    // If we've gotten here we could not parse the number
    errors.add(new ScopedLocalizableError("converter.number", "invalidNumber"));
    return null;
  }
  /** Returns the date for {@code value}. Returns null if the value couldn't be parsed. */
  public static Date parse(String value) {
    if (value.length() == 0) {
      return null;
    }

    ParsePosition position = new ParsePosition(0);
    Date result = STANDARD_DATE_FORMAT.get().parse(value, position);
    if (position.getIndex() == value.length()) {
      // STANDARD_DATE_FORMAT must match exactly; all text must be consumed, e.g. no ignored
      // non-standard trailing "+01:00". Those cases are covered below.
      return result;
    }
    synchronized (BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS) {
      for (int i = 0, count = BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS.length; i < count; i++) {
        DateFormat format = BROWSER_COMPATIBLE_DATE_FORMATS[i];
        if (format == null) {
          format = new SimpleDateFormat(BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS[i], Locale.US);
          // Set the timezone to use when interpreting formats that don't have a timezone. GMT is
          // specified by RFC 2616.
          format.setTimeZone(GMT);
          BROWSER_COMPATIBLE_DATE_FORMATS[i] = format;
        }
        position.setIndex(0);
        result = format.parse(value, position);
        if (position.getIndex() != 0) {
          // Something was parsed. It's possible the entire string was not consumed but we ignore
          // that. If any of the BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS ended in "'GMT'" we'd have
          // to also check that position.getIndex() == value.length() otherwise parsing might have
          // terminated early, ignoring things like "+01:00". Leaving this as != 0 means that any
          // trailing junk is ignored.
          return result;
        }
      }
    }
    return null;
  }
  protected void load() throws IOException {

    BufferedReader is = new BufferedReader(new FileReader("ReminderService.txt"));
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy MM dd hh mm");
    String aLine;
    while ((aLine = is.readLine()) != null) {
      ParsePosition pp = new ParsePosition(0);
      Date date = formatter.parse(aLine, pp);
      if (date == null) {
        message("Invalid date in " + aLine);
        continue;
      }
      String mesg = aLine.substring(pp.getIndex());
      l.add(new Item(date, mesg));
    }
  }
  @Override
  public Result<Date> convertToModel(String value, ValueContext context) {
    if (value == null) {
      return Result.ok(null);
    }

    // Remove leading and trailing white space
    value = value.trim();

    ParsePosition parsePosition = new ParsePosition(0);
    Date parsedValue = getFormat(context.getLocale().orElse(null)).parse(value, parsePosition);
    if (parsePosition.getIndex() != value.length()) {
      return Result.error("Could not convert '" + value);
    }

    return Result.ok(parsedValue);
  }
  /**
   * Parses a string using {@link SimpleDateFormat} and a given pattern, and if present, parses a
   * fractional seconds component. The fractional seconds component must begin with a decimal point
   * ('.') followed by numeric digits. The precision is rounded to a maximum of 3 digits of
   * fractional seconds precision (to obtain milliseconds).
   *
   * @param s string to be parsed
   * @param pattern {@link SimpleDateFormat} pattern
   * @param tz time zone in which to interpret string. Defaults to the local time zone
   * @return a {@link DateTimeUtils.PrecisionTime PrecisionTime} initialized with the parsed value,
   *     or null if parsing failed. The PrecisionTime contains a GMT Calendar and a precision.
   */
  public static PrecisionTime parsePrecisionDateTimeLiteral(String s, String pattern, TimeZone tz) {
    assert pattern != null;
    ParsePosition pp = new ParsePosition(0);
    Calendar cal = parseDateFormat(s, pattern, tz, pp);
    if (cal == null) {
      return null; // Invalid date/time format
    }

    // Note: the Java SimpleDateFormat 'S' treats any number after
    // the decimal as milliseconds. That means 12:00:00.9 has 9
    // milliseconds and 12:00:00.9999 has 9999 milliseconds.
    int p = 0;
    if (pp.getIndex() < s.length()) {
      // Check to see if rest is decimal portion
      if (s.charAt(pp.getIndex()) != '.') {
        return null;
      }

      // Skip decimal sign
      pp.setIndex(pp.getIndex() + 1);

      // Parse decimal portion
      if (pp.getIndex() < s.length()) {
        String secFraction = s.substring(pp.getIndex());
        if (!secFraction.matches("\\d+")) {
          return null;
        }
        NumberFormat nf = NumberFormat.getIntegerInstance();
        Number num = nf.parse(s, pp);
        if ((num == null) || (pp.getIndex() != s.length())) {
          // Invalid decimal portion
          return null;
        }

        // Determine precision - only support prec 3 or lower
        // (milliseconds) Higher precisions are quietly rounded away
        p = Math.min(3, secFraction.length());

        // Calculate milliseconds
        int ms = (int) Math.round(num.longValue() * Math.pow(10, 3 - secFraction.length()));
        cal.add(Calendar.MILLISECOND, ms);
      }
    }

    assert pp.getIndex() == s.length();
    PrecisionTime ret = new PrecisionTime(cal, p);
    return ret;
  }