Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8032491

DateTimeFormatter fixed width adjacent value parsing does not match spec

    Details

    • Type: Bug
    • Status: Resolved
    • Priority: P3
    • Resolution: Fixed
    • Affects Version/s: 9
    • Fix Version/s: 9
    • Component/s: core-libs
    • Labels:
      None
    • Subcomponent:
    • Introduced In Version:
      8
    • Resolved In Build:
      b08

      Backports

        Description

        The adjacent value parsing spec includes the phrase "If adjacent parsing is active, then parsing must match exactly the specified number of digits in both strict and lenient modes." in the method appendValue(TemporalField field, int width). Unfortunately, the spec is not correctly implemented when the first field in an adjacent parsing set is fixed width.

        In strict mode, the first field in an adjacent parsing set uses its fixed width correctly producing the correct result. However it uses an inefficient double parse internally.

        In lenient mode the first field in an adjacent parsing set is converted to be variable width producing the wrong result, and not conforming to the spec.

        This can be noticed using a pattern of HHmm'9' which parses in strict mode but fails in lenient mode.

        Discovered via JDK-8031085.

          Issue Links

            Activity

            Hide
            scolebourne Stephen Colebourne added a comment - - edited
            Patch for issue (second version)

            diff --git a/src/share/classes/java/time/format/DateTimeFormatterBuilder.java b/src/share/classes/java/time/format/DateTimeFormatterBuilder.java
            --- a/src/share/classes/java/time/format/DateTimeFormatterBuilder.java
            +++ b/src/share/classes/java/time/format/DateTimeFormatterBuilder.java
            @@ -2595,8 +2595,9 @@
                         return value;
                     }
             
            - boolean isFixedWidth() {
            - return subsequentWidth == -1;
            + boolean isFixedWidth(DateTimeParseContext context) {
            + return subsequentWidth == -1 ||
            + (subsequentWidth > 0 && minWidth == maxWidth && signStyle == SignStyle.NOT_NEGATIVE);
                     }
             
                     @Override
            @@ -2625,12 +2626,12 @@
                                 return ~position;
                             }
                         }
            - int effMinWidth = (context.isStrict() || isFixedWidth() ? minWidth : 1);
            + int effMinWidth = (context.isStrict() || isFixedWidth(context) ? minWidth : 1);
                         int minEndPos = position + effMinWidth;
                         if (minEndPos > length) {
                             return ~position;
                         }
            - int effMaxWidth = (context.isStrict() || isFixedWidth() ? maxWidth : 9) + Math.max(subsequentWidth, 0);
            + int effMaxWidth = (context.isStrict() || isFixedWidth(context) ? maxWidth : 9) + Math.max(subsequentWidth, 0);
                         long total = 0;
                         BigInteger totalBig = null;
                         int pos = position;
            @@ -2855,6 +2856,13 @@
                                 this.subsequentWidth + subsequentWidth);
                     }
             
            + boolean isFixedWidth(DateTimeParseContext context) {
            + if (context.isStrict() == false) {
            + return false;
            + }
            + return super.isFixedWidth(context);
            + }
            +
                     @Override
                     public String toString() {
                         return "ReducedValue(" + field + "," + minWidth + "," + maxWidth + "," + (baseDate != null ? baseDate : baseValue) + ")";
            diff --git a/test/java/time/tck/java/time/format/TCKDateTimeFormatterBuilder.java b/test/java/time/tck/java/time/format/TCKDateTimeFormatterBuilder.java
            --- a/test/java/time/tck/java/time/format/TCKDateTimeFormatterBuilder.java
            +++ b/test/java/time/tck/java/time/format/TCKDateTimeFormatterBuilder.java
            @@ -60,11 +60,14 @@
             package tck.java.time.format;
             
             import static java.time.temporal.ChronoField.DAY_OF_MONTH;
            +import static java.time.temporal.ChronoField.HOUR_OF_DAY;
             import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
             import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
            +import static java.time.temporal.ChronoField.NANO_OF_SECOND;
             import static java.time.temporal.ChronoField.YEAR;
             import static org.testng.Assert.assertEquals;
             
            +import java.text.ParsePosition;
             import java.time.LocalDate;
             import java.time.YearMonth;
             import java.time.ZoneOffset;
            @@ -73,6 +76,7 @@
             import java.time.format.SignStyle;
             import java.time.format.TextStyle;
             import java.time.temporal.Temporal;
            +import java.time.temporal.TemporalAccessor;
             import java.util.HashMap;
             import java.util.Locale;
             import java.util.Map;
            @@ -728,4 +732,152 @@
                     return LocalDate.of(y, m, d);
                 }
             
            + //-----------------------------------------------------------------------
            + @Test
            + public void test_adjacent_strict_firstFixedWidth() throws Exception {
            + // succeeds because both number elements are fixed width
            + DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
            + ParsePosition pp = new ParsePosition(0);
            + TemporalAccessor parsed = f.parseUnresolved("12309", pp);
            + assertEquals(pp.getErrorIndex(), -1);
            + assertEquals(pp.getIndex(), 5);
            + assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
            + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
            + }
            +
            + @Test
            + public void test_adjacent_strict_firstVariableWidth_success() throws Exception {
            + // succeeds greedily parsing variable width, then fixed width, to non-numeric Z
            + DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('Z').toFormatter(Locale.UK);
            + ParsePosition pp = new ParsePosition(0);
            + TemporalAccessor parsed = f.parseUnresolved("12309Z", pp);
            + assertEquals(pp.getErrorIndex(), -1);
            + assertEquals(pp.getIndex(), 6);
            + assertEquals(parsed.getLong(HOUR_OF_DAY), 123L);
            + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 9L);
            + }
            +
            + @Test
            + public void test_adjacent_strict_firstVariableWidth_fails() throws Exception {
            + // fails because literal is a number and variable width parse greedily absorbs it
            + DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
            + ParsePosition pp = new ParsePosition(0);
            + TemporalAccessor parsed = f.parseUnresolved("12309", pp);
            + assertEquals(pp.getErrorIndex(), 5);
            + assertEquals(parsed, null);
            + }
            +
            + @Test
            + public void test_adjacent_lenient() throws Exception {
            + // succeeds because both number elements are fixed width even in lenient mode
            + DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
            + ParsePosition pp = new ParsePosition(0);
            + TemporalAccessor parsed = f.parseUnresolved("12309", pp);
            + assertEquals(pp.getErrorIndex(), -1);
            + assertEquals(pp.getIndex(), 5);
            + assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
            + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
            + }
            +
            + @Test
            + public void test_adjacent_lenient_firstVariableWidth_success() throws Exception {
            + // succeeds greedily parsing variable width, then fixed width, to non-numeric Z
            + DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('Z').toFormatter(Locale.UK);
            + ParsePosition pp = new ParsePosition(0);
            + TemporalAccessor parsed = f.parseUnresolved("12309Z", pp);
            + assertEquals(pp.getErrorIndex(), -1);
            + assertEquals(pp.getIndex(), 6);
            + assertEquals(parsed.getLong(HOUR_OF_DAY), 123L);
            + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 9L);
            + }
            +
            + @Test
            + public void test_adjacent_lenient_firstVariableWidth_fails() throws Exception {
            + // fails because literal is a number and variable width parse greedily absorbs it
            + DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
            + ParsePosition pp = new ParsePosition(0);
            + TemporalAccessor parsed = f.parseUnresolved("12309", pp);
            + assertEquals(pp.getErrorIndex(), 5);
            + assertEquals(parsed, null);
            + }
            +
            + //-----------------------------------------------------------------------
            + @Test
            + public void test_adjacent_strict_fractionFollows() throws Exception {
            + // succeeds because hour/min are fixed width
            + DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK);
            + ParsePosition pp = new ParsePosition(0);
            + TemporalAccessor parsed = f.parseUnresolved("1230567", pp);
            + assertEquals(pp.getErrorIndex(), -1);
            + assertEquals(pp.getIndex(), 7);
            + assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
            + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
            + assertEquals(parsed.getLong(NANO_OF_SECOND), 567_000_000L);
            + }
            +
            + @Test
            + public void test_adjacent_strict_fractionFollows_2digit() throws Exception {
            + // succeeds because hour/min are fixed width
            + DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK);
            + ParsePosition pp = new ParsePosition(0);
            + TemporalAccessor parsed = f.parseUnresolved("123056", pp);
            + assertEquals(pp.getErrorIndex(), -1);
            + assertEquals(pp.getIndex(), 6);
            + assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
            + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
            + assertEquals(parsed.getLong(NANO_OF_SECOND), 560_000_000L);
            + }
            +
            + @Test
            + public void test_adjacent_strict_fractionFollows_0digit() throws Exception {
            + // succeeds because hour/min are fixed width
            + DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK);
            + ParsePosition pp = new ParsePosition(0);
            + TemporalAccessor parsed = f.parseUnresolved("1230", pp);
            + assertEquals(pp.getErrorIndex(), -1);
            + assertEquals(pp.getIndex(), 4);
            + assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
            + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
            + // assertEquals(parsed.getLong(NANO_OF_SECOND), 0L);
            + }
            +
            + @Test
            + public void test_adjacent_lenient_fractionFollows() throws Exception {
            + // succeeds because hour/min are fixed width
            + DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK);
            + ParsePosition pp = new ParsePosition(0);
            + TemporalAccessor parsed = f.parseUnresolved("1230567", pp);
            + assertEquals(pp.getErrorIndex(), -1);
            + assertEquals(pp.getIndex(), 7);
            + assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
            + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
            + assertEquals(parsed.getLong(NANO_OF_SECOND), 567_000_000L);
            + }
            +
            + @Test
            + public void test_adjacent_lenient_fractionFollows_2digit() throws Exception {
            + // succeeds because hour/min are fixed width
            + DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK);
            + ParsePosition pp = new ParsePosition(0);
            + TemporalAccessor parsed = f.parseUnresolved("123056", pp);
            + assertEquals(pp.getErrorIndex(), -1);
            + assertEquals(pp.getIndex(), 6);
            + assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
            + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
            + assertEquals(parsed.getLong(NANO_OF_SECOND), 560_000_000L);
            + }
            +
            + @Test
            + public void test_adjacent_lenient_fractionFollows_0digit() throws Exception {
            + // succeeds because hour/min are fixed width
            + DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK);
            + ParsePosition pp = new ParsePosition(0);
            + TemporalAccessor parsed = f.parseUnresolved("1230", pp);
            + assertEquals(pp.getErrorIndex(), -1);
            + assertEquals(pp.getIndex(), 4);
            + assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
            + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
            + // assertEquals(parsed.getLong(NANO_OF_SECOND), 0L);
            + }
            +
             }
            diff --git a/test/java/time/test/java/time/format/TestReducedParser.java b/test/java/time/test/java/time/format/TestReducedParser.java
            --- a/test/java/time/test/java/time/format/TestReducedParser.java
            +++ b/test/java/time/test/java/time/format/TestReducedParser.java
            @@ -354,7 +354,7 @@
                         {"yyMMdd", "200703", STRICT, 0, 6, 2020, 7, 3},
                         {"ddMMyy", "230714", LENIENT, 0, 6, 2014, 7, 23},
                         {"ddMMyy", "230714", STRICT, 0, 6, 2014, 7, 23},
            - {"ddMMyy", "25062001", LENIENT, 0, 8, 2001, 20, 2506},
            + {"ddMMyy", "25062001", LENIENT, 0, 8, 2001, 6, 25},
                         {"ddMMyy", "25062001", STRICT, 0, 6, 2020, 6, 25},
                         {"ddMMy", "27052002", LENIENT, 0, 8, 2002, 5, 27},
                         {"ddMMy", "27052002", STRICT, 0, 8, 2002, 5, 27},
            Show
            scolebourne Stephen Colebourne added a comment - - edited Patch for issue (second version) diff --git a/src/share/classes/java/time/format/DateTimeFormatterBuilder.java b/src/share/classes/java/time/format/DateTimeFormatterBuilder.java --- a/src/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/src/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -2595,8 +2595,9 @@              return value;          }   - boolean isFixedWidth() { - return subsequentWidth == -1; + boolean isFixedWidth(DateTimeParseContext context) { + return subsequentWidth == -1 || + (subsequentWidth > 0 && minWidth == maxWidth && signStyle == SignStyle.NOT_NEGATIVE);          }            @Override @@ -2625,12 +2626,12 @@                      return ~position;                  }              } - int effMinWidth = (context.isStrict() || isFixedWidth() ? minWidth : 1); + int effMinWidth = (context.isStrict() || isFixedWidth(context) ? minWidth : 1);              int minEndPos = position + effMinWidth;              if (minEndPos > length) {                  return ~position;              } - int effMaxWidth = (context.isStrict() || isFixedWidth() ? maxWidth : 9) + Math.max(subsequentWidth, 0); + int effMaxWidth = (context.isStrict() || isFixedWidth(context) ? maxWidth : 9) + Math.max(subsequentWidth, 0);              long total = 0;              BigInteger totalBig = null;              int pos = position; @@ -2855,6 +2856,13 @@                      this.subsequentWidth + subsequentWidth);          }   + boolean isFixedWidth(DateTimeParseContext context) { + if (context.isStrict() == false) { + return false; + } + return super.isFixedWidth(context); + } +          @Override          public String toString() {              return "ReducedValue(" + field + "," + minWidth + "," + maxWidth + "," + (baseDate != null ? baseDate : baseValue) + ")"; diff --git a/test/java/time/tck/java/time/format/TCKDateTimeFormatterBuilder.java b/test/java/time/tck/java/time/format/TCKDateTimeFormatterBuilder.java --- a/test/java/time/tck/java/time/format/TCKDateTimeFormatterBuilder.java +++ b/test/java/time/tck/java/time/format/TCKDateTimeFormatterBuilder.java @@ -60,11 +60,14 @@  package tck.java.time.format;    import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.HOUR_OF_DAY;  import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;  import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.NANO_OF_SECOND;  import static java.time.temporal.ChronoField.YEAR;  import static org.testng.Assert.assertEquals;   +import java.text.ParsePosition;  import java.time.LocalDate;  import java.time.YearMonth;  import java.time.ZoneOffset; @@ -73,6 +76,7 @@  import java.time.format.SignStyle;  import java.time.format.TextStyle;  import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor;  import java.util.HashMap;  import java.util.Locale;  import java.util.Map; @@ -728,4 +732,152 @@          return LocalDate.of(y, m, d);      }   + //----------------------------------------------------------------------- + @Test + public void test_adjacent_strict_firstFixedWidth() throws Exception { + // succeeds because both number elements are fixed width + DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK); + ParsePosition pp = new ParsePosition(0); + TemporalAccessor parsed = f.parseUnresolved("12309", pp); + assertEquals(pp.getErrorIndex(), -1); + assertEquals(pp.getIndex(), 5); + assertEquals(parsed.getLong(HOUR_OF_DAY), 12L); + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L); + } + + @Test + public void test_adjacent_strict_firstVariableWidth_success() throws Exception { + // succeeds greedily parsing variable width, then fixed width, to non-numeric Z + DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('Z').toFormatter(Locale.UK); + ParsePosition pp = new ParsePosition(0); + TemporalAccessor parsed = f.parseUnresolved("12309Z", pp); + assertEquals(pp.getErrorIndex(), -1); + assertEquals(pp.getIndex(), 6); + assertEquals(parsed.getLong(HOUR_OF_DAY), 123L); + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 9L); + } + + @Test + public void test_adjacent_strict_firstVariableWidth_fails() throws Exception { + // fails because literal is a number and variable width parse greedily absorbs it + DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK); + ParsePosition pp = new ParsePosition(0); + TemporalAccessor parsed = f.parseUnresolved("12309", pp); + assertEquals(pp.getErrorIndex(), 5); + assertEquals(parsed, null); + } + + @Test + public void test_adjacent_lenient() throws Exception { + // succeeds because both number elements are fixed width even in lenient mode + DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK); + ParsePosition pp = new ParsePosition(0); + TemporalAccessor parsed = f.parseUnresolved("12309", pp); + assertEquals(pp.getErrorIndex(), -1); + assertEquals(pp.getIndex(), 5); + assertEquals(parsed.getLong(HOUR_OF_DAY), 12L); + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L); + } + + @Test + public void test_adjacent_lenient_firstVariableWidth_success() throws Exception { + // succeeds greedily parsing variable width, then fixed width, to non-numeric Z + DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('Z').toFormatter(Locale.UK); + ParsePosition pp = new ParsePosition(0); + TemporalAccessor parsed = f.parseUnresolved("12309Z", pp); + assertEquals(pp.getErrorIndex(), -1); + assertEquals(pp.getIndex(), 6); + assertEquals(parsed.getLong(HOUR_OF_DAY), 123L); + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 9L); + } + + @Test + public void test_adjacent_lenient_firstVariableWidth_fails() throws Exception { + // fails because literal is a number and variable width parse greedily absorbs it + DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK); + ParsePosition pp = new ParsePosition(0); + TemporalAccessor parsed = f.parseUnresolved("12309", pp); + assertEquals(pp.getErrorIndex(), 5); + assertEquals(parsed, null); + } + + //----------------------------------------------------------------------- + @Test + public void test_adjacent_strict_fractionFollows() throws Exception { + // succeeds because hour/min are fixed width + DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK); + ParsePosition pp = new ParsePosition(0); + TemporalAccessor parsed = f.parseUnresolved("1230567", pp); + assertEquals(pp.getErrorIndex(), -1); + assertEquals(pp.getIndex(), 7); + assertEquals(parsed.getLong(HOUR_OF_DAY), 12L); + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L); + assertEquals(parsed.getLong(NANO_OF_SECOND), 567_000_000L); + } + + @Test + public void test_adjacent_strict_fractionFollows_2digit() throws Exception { + // succeeds because hour/min are fixed width + DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK); + ParsePosition pp = new ParsePosition(0); + TemporalAccessor parsed = f.parseUnresolved("123056", pp); + assertEquals(pp.getErrorIndex(), -1); + assertEquals(pp.getIndex(), 6); + assertEquals(parsed.getLong(HOUR_OF_DAY), 12L); + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L); + assertEquals(parsed.getLong(NANO_OF_SECOND), 560_000_000L); + } + + @Test + public void test_adjacent_strict_fractionFollows_0digit() throws Exception { + // succeeds because hour/min are fixed width + DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK); + ParsePosition pp = new ParsePosition(0); + TemporalAccessor parsed = f.parseUnresolved("1230", pp); + assertEquals(pp.getErrorIndex(), -1); + assertEquals(pp.getIndex(), 4); + assertEquals(parsed.getLong(HOUR_OF_DAY), 12L); + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L); + // assertEquals(parsed.getLong(NANO_OF_SECOND), 0L); + } + + @Test + public void test_adjacent_lenient_fractionFollows() throws Exception { + // succeeds because hour/min are fixed width + DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK); + ParsePosition pp = new ParsePosition(0); + TemporalAccessor parsed = f.parseUnresolved("1230567", pp); + assertEquals(pp.getErrorIndex(), -1); + assertEquals(pp.getIndex(), 7); + assertEquals(parsed.getLong(HOUR_OF_DAY), 12L); + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L); + assertEquals(parsed.getLong(NANO_OF_SECOND), 567_000_000L); + } + + @Test + public void test_adjacent_lenient_fractionFollows_2digit() throws Exception { + // succeeds because hour/min are fixed width + DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK); + ParsePosition pp = new ParsePosition(0); + TemporalAccessor parsed = f.parseUnresolved("123056", pp); + assertEquals(pp.getErrorIndex(), -1); + assertEquals(pp.getIndex(), 6); + assertEquals(parsed.getLong(HOUR_OF_DAY), 12L); + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L); + assertEquals(parsed.getLong(NANO_OF_SECOND), 560_000_000L); + } + + @Test + public void test_adjacent_lenient_fractionFollows_0digit() throws Exception { + // succeeds because hour/min are fixed width + DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK); + ParsePosition pp = new ParsePosition(0); + TemporalAccessor parsed = f.parseUnresolved("1230", pp); + assertEquals(pp.getErrorIndex(), -1); + assertEquals(pp.getIndex(), 4); + assertEquals(parsed.getLong(HOUR_OF_DAY), 12L); + assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L); + // assertEquals(parsed.getLong(NANO_OF_SECOND), 0L); + } +  } diff --git a/test/java/time/test/java/time/format/TestReducedParser.java b/test/java/time/test/java/time/format/TestReducedParser.java --- a/test/java/time/test/java/time/format/TestReducedParser.java +++ b/test/java/time/test/java/time/format/TestReducedParser.java @@ -354,7 +354,7 @@              {"yyMMdd", "200703", STRICT, 0, 6, 2020, 7, 3},              {"ddMMyy", "230714", LENIENT, 0, 6, 2014, 7, 23},              {"ddMMyy", "230714", STRICT, 0, 6, 2014, 7, 23}, - {"ddMMyy", "25062001", LENIENT, 0, 8, 2001, 20, 2506}, + {"ddMMyy", "25062001", LENIENT, 0, 8, 2001, 6, 25},              {"ddMMyy", "25062001", STRICT, 0, 6, 2020, 6, 25},              {"ddMMy", "27052002", LENIENT, 0, 8, 2002, 5, 27},              {"ddMMy", "27052002", STRICT, 0, 8, 2002, 5, 27},
            Hide
            hgupdate HG Updates added a comment -
            URL: http://hg.openjdk.java.net/jdk9/dev/jdk/rev/357781084a1b
            User: rriggs
            Date: 2014-03-29 19:13:59 +0000
            Show
            hgupdate HG Updates added a comment - URL: http://hg.openjdk.java.net/jdk9/dev/jdk/rev/357781084a1b User: rriggs Date: 2014-03-29 19:13:59 +0000
            Hide
            hgupdate HG Updates added a comment -
            URL: http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/357781084a1b
            User: lana
            Date: 2014-04-09 18:28:21 +0000
            Show
            hgupdate HG Updates added a comment - URL: http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/357781084a1b User: lana Date: 2014-04-09 18:28:21 +0000

              People

              • Assignee:
                rriggs Roger Riggs
                Reporter:
                scolebourne Stephen Colebourne
              • Votes:
                0 Vote for this issue
                Watchers:
                2 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: