/** * 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; }
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()); }
/** 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); } }
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; }
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); }
/** * 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()); }
/** * 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; }
/* (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; }
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()); } }
/** * 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(); }
/** * 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; }
@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; }
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; }