386 public static Duration parse(CharSequence text) { |
386 public static Duration parse(CharSequence text) { |
387 Objects.requireNonNull(text, "text"); |
387 Objects.requireNonNull(text, "text"); |
388 Matcher matcher = PATTERN.matcher(text); |
388 Matcher matcher = PATTERN.matcher(text); |
389 if (matcher.matches()) { |
389 if (matcher.matches()) { |
390 // check for letter T but no time sections |
390 // check for letter T but no time sections |
391 if ("T".equals(matcher.group(3)) == false) { |
391 if (!charMatch(text, matcher.start(3), matcher.end(3), 'T')) { |
392 boolean negate = "-".equals(matcher.group(1)); |
392 boolean negate = charMatch(text, matcher.start(1), matcher.end(1), '-'); |
393 String dayMatch = matcher.group(2); |
393 |
394 String hourMatch = matcher.group(4); |
394 int dayStart = matcher.start(2), dayEnd = matcher.end(2); |
395 String minuteMatch = matcher.group(5); |
395 int hourStart = matcher.start(4), hourEnd = matcher.end(4); |
396 String secondMatch = matcher.group(6); |
396 int minuteStart = matcher.start(5), minuteEnd = matcher.end(5); |
397 String fractionMatch = matcher.group(7); |
397 int secondStart = matcher.start(6), secondEnd = matcher.end(6); |
398 if (dayMatch != null || hourMatch != null || minuteMatch != null || secondMatch != null) { |
398 int fractionStart = matcher.start(7), fractionEnd = matcher.end(7); |
399 long daysAsSecs = parseNumber(text, dayMatch, SECONDS_PER_DAY, "days"); |
399 |
400 long hoursAsSecs = parseNumber(text, hourMatch, SECONDS_PER_HOUR, "hours"); |
400 if (dayStart >= 0 || hourStart >= 0 || minuteStart >= 0 || secondStart >= 0) { |
401 long minsAsSecs = parseNumber(text, minuteMatch, SECONDS_PER_MINUTE, "minutes"); |
401 long daysAsSecs = parseNumber(text, dayStart, dayEnd, SECONDS_PER_DAY, "days"); |
402 long seconds = parseNumber(text, secondMatch, 1, "seconds"); |
402 long hoursAsSecs = parseNumber(text, hourStart, hourEnd, SECONDS_PER_HOUR, "hours"); |
403 int nanos = parseFraction(text, fractionMatch, seconds < 0 ? -1 : 1); |
403 long minsAsSecs = parseNumber(text, minuteStart, minuteEnd, SECONDS_PER_MINUTE, "minutes"); |
|
404 long seconds = parseNumber(text, secondStart, secondEnd, 1, "seconds"); |
|
405 int nanos = parseFraction(text, fractionStart, fractionEnd, seconds < 0 ? -1 : 1); |
404 try { |
406 try { |
405 return create(negate, daysAsSecs, hoursAsSecs, minsAsSecs, seconds, nanos); |
407 return create(negate, daysAsSecs, hoursAsSecs, minsAsSecs, seconds, nanos); |
406 } catch (ArithmeticException ex) { |
408 } catch (ArithmeticException ex) { |
407 throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Duration: overflow", text, 0).initCause(ex); |
409 throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Duration: overflow", text, 0).initCause(ex); |
408 } |
410 } |
410 } |
412 } |
411 } |
413 } |
412 throw new DateTimeParseException("Text cannot be parsed to a Duration", text, 0); |
414 throw new DateTimeParseException("Text cannot be parsed to a Duration", text, 0); |
413 } |
415 } |
414 |
416 |
415 private static long parseNumber(CharSequence text, String parsed, int multiplier, String errorText) { |
417 private static boolean charMatch(CharSequence text, int start, int end, char c) { |
|
418 return (start >= 0 && end == start + 1 && text.charAt(start) == c); |
|
419 } |
|
420 |
|
421 private static long parseNumber(CharSequence text, int start, int end, int multiplier, String errorText) { |
416 // regex limits to [-+]?[0-9]+ |
422 // regex limits to [-+]?[0-9]+ |
417 if (parsed == null) { |
423 if (start < 0 || end < 0) { |
418 return 0; |
424 return 0; |
419 } |
425 } |
420 try { |
426 try { |
421 long val = Long.parseLong(parsed); |
427 long val = Long.parseLong(text, 10, start, end); |
422 return Math.multiplyExact(val, multiplier); |
428 return Math.multiplyExact(val, multiplier); |
423 } catch (NumberFormatException | ArithmeticException ex) { |
429 } catch (NumberFormatException | ArithmeticException ex) { |
424 throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Duration: " + errorText, text, 0).initCause(ex); |
430 throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Duration: " + errorText, text, 0).initCause(ex); |
425 } |
431 } |
426 } |
432 } |
427 |
433 |
428 private static int parseFraction(CharSequence text, String parsed, int negate) { |
434 private static int parseFraction(CharSequence text, int start, int end, int negate) { |
429 // regex limits to [0-9]{0,9} |
435 // regex limits to [0-9]{0,9} |
430 if (parsed == null || parsed.length() == 0) { |
436 if (start < 0 || end < 0 || end - start == 0) { |
431 return 0; |
437 return 0; |
432 } |
438 } |
433 try { |
439 try { |
434 parsed = (parsed + "000000000").substring(0, 9); |
440 int fraction = Integer.parseInt(text, 10, start, end); |
435 return Integer.parseInt(parsed) * negate; |
441 |
|
442 // for number strings smaller than 9 digits, interpret as if there |
|
443 // were trailing zeros |
|
444 for (int i = end - start; i < 9; i++) { |
|
445 fraction *= 10; |
|
446 } |
|
447 return fraction * negate; |
436 } catch (NumberFormatException | ArithmeticException ex) { |
448 } catch (NumberFormatException | ArithmeticException ex) { |
437 throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Duration: fraction", text, 0).initCause(ex); |
449 throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Duration: fraction", text, 0).initCause(ex); |
438 } |
450 } |
439 } |
451 } |
440 |
452 |