test/java/time/tck/java/time/format/TCKDateTimeFormatter.java

Print this page

        

@@ -58,10 +58,12 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 package tck.java.time.format;
 
 import static java.time.temporal.ChronoField.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.DAY_OF_WEEK;
+import static java.time.temporal.ChronoField.DAY_OF_YEAR;
 import static java.time.temporal.ChronoField.HOUR_OF_DAY;
 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
 import static java.time.temporal.ChronoField.YEAR;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNull;

@@ -69,42 +71,50 @@
 import static org.testng.Assert.fail;
 
 import java.text.Format;
 import java.text.ParseException;
 import java.text.ParsePosition;
-import java.util.Locale;
-
 import java.time.DateTimeException;
+import java.time.DayOfWeek;
 import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.YearMonth;
 import java.time.ZoneId;
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
+import java.time.chrono.ChronoZonedDateTime;
+import java.time.chrono.Chronology;
+import java.time.chrono.IsoChronology;
 import java.time.chrono.ThaiBuddhistChronology;
+import java.time.chrono.ThaiBuddhistDate;
 import java.time.format.DateTimeFormatSymbols;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatterBuilder;
 import java.time.format.DateTimeParseException;
 import java.time.format.SignStyle;
-import java.time.chrono.Chronology;
-import java.time.chrono.IsoChronology;
-import java.time.OffsetDateTime;
-import java.time.OffsetTime;
-import java.time.temporal.Temporal;
+import java.time.temporal.IsoFields;
 import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalField;
 import java.time.temporal.TemporalQuery;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
 
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 /**
  * Test DateTimeFormatter.
  */
-@Test(groups={"tck"})
+@Test
 public class TCKDateTimeFormatter {
 
     private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1);
     private static final ZoneOffset OFFSET_PTHREE = ZoneOffset.ofHours(3);
     private static final ZoneId ZONE_PARIS = ZoneId.of("Europe/Paris");

@@ -158,91 +168,245 @@
         test = test.withZone(null);
         assertEquals(test.getZone(), null);
     }
 
     //-----------------------------------------------------------------------
-    // print
+    @Test
+    public void test_resolverFields_selectOneDateResolveYMD() throws Exception {
+        DateTimeFormatter base = new DateTimeFormatterBuilder()
+                .appendValue(YEAR).appendLiteral('-').appendValue(MONTH_OF_YEAR).appendLiteral('-')
+                .appendValue(DAY_OF_MONTH).appendLiteral('-').appendValue(DAY_OF_YEAR).toFormatter();
+        DateTimeFormatter f = base.withResolverFields(YEAR, MONTH_OF_YEAR, DAY_OF_MONTH);
+        try {
+            base.parse("2012-6-30-321", LocalDate::from);  // wrong day-of-year
+            fail();
+        } catch (DateTimeException ex) {
+            // expected, fails as it produces two different dates
+        }
+        LocalDate parsed = f.parse("2012-6-30-321", LocalDate::from);  // ignored day-of-year
+        assertEquals(parsed, LocalDate.of(2012, 6, 30));
+    }
+
+    @Test
+    public void test_resolverFields_selectOneDateResolveYD() throws Exception {
+        DateTimeFormatter base = new DateTimeFormatterBuilder()
+                .appendValue(YEAR).appendLiteral('-').appendValue(MONTH_OF_YEAR).appendLiteral('-')
+                .appendValue(DAY_OF_MONTH).appendLiteral('-').appendValue(DAY_OF_YEAR).toFormatter();
+        DateTimeFormatter f = base.withResolverFields(YEAR, DAY_OF_YEAR);
+        assertEquals(f.getResolverFields(), new HashSet<>(Arrays.asList(YEAR, DAY_OF_YEAR)));
+        try {
+            base.parse("2012-6-30-321", LocalDate::from);  // wrong month/day-of-month
+            fail();
+        } catch (DateTimeException ex) {
+            // expected, fails as it produces two different dates
+        }
+        LocalDate parsed = f.parse("2012-6-30-321", LocalDate::from);  // ignored month/day-of-month
+        assertEquals(parsed, LocalDate.of(2012, 11, 16));
+    }
+
+    @Test
+    public void test_resolverFields_ignoreCrossCheck() throws Exception {
+        DateTimeFormatter base = new DateTimeFormatterBuilder()
+                .appendValue(YEAR).appendLiteral('-').appendValue(DAY_OF_YEAR).appendLiteral('-')
+                .appendValue(DAY_OF_WEEK).toFormatter();
+        DateTimeFormatter f = base.withResolverFields(YEAR, DAY_OF_YEAR);
+        try {
+            base.parse("2012-321-1", LocalDate::from);  // wrong day-of-week
+            fail();
+        } catch (DateTimeException ex) {
+            // expected, should fail in cross-check of day-of-week
+        }
+        LocalDate parsed = f.parse("2012-321-1", LocalDate::from);  // ignored wrong day-of-week
+        assertEquals(parsed, LocalDate.of(2012, 11, 16));
+    }
+
+    @Test
+    public void test_resolverFields_emptyList() throws Exception {
+        DateTimeFormatter f = new DateTimeFormatterBuilder()
+                .appendValue(YEAR).toFormatter().withResolverFields();
+        TemporalAccessor parsed = f.parse("2012");
+        assertEquals(parsed.isSupported(YEAR), false);  // not in the list of resolverFields
+    }
+
+    @Test
+    public void test_resolverFields_listOfOneMatching() throws Exception {
+        DateTimeFormatter f = new DateTimeFormatterBuilder()
+                .appendValue(YEAR).toFormatter().withResolverFields(YEAR);
+        TemporalAccessor parsed = f.parse("2012");
+        assertEquals(parsed.isSupported(YEAR), true);
+    }
+
+    @Test
+    public void test_resolverFields_listOfOneNotMatching() throws Exception {
+        DateTimeFormatter f = new DateTimeFormatterBuilder()
+                .appendValue(YEAR).toFormatter().withResolverFields(MONTH_OF_YEAR);
+        TemporalAccessor parsed = f.parse("2012");
+        assertEquals(parsed.isSupported(YEAR), false);  // not in the list of resolverFields
+        assertEquals(parsed.isSupported(MONTH_OF_YEAR), false);
+    }
+
+    @Test
+    public void test_resolverFields_listOfOneNull() throws Exception {
+        DateTimeFormatter f = new DateTimeFormatterBuilder()
+                .appendValue(YEAR).toFormatter().withResolverFields((TemporalField) null);
+        TemporalAccessor parsed = f.parse("2012");
+        assertEquals(parsed.isSupported(YEAR), false);  // not in the list of resolverFields
+    }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void test_resolverFields_Array_null() throws Exception {
+        DateTimeFormatter.ISO_DATE.withResolverFields((TemporalField[]) null);
+    }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void test_resolverFields_Set_null() throws Exception {
+        DateTimeFormatter.ISO_DATE.withResolverFields((Set<TemporalField>) null);
+    }
+
+    //-----------------------------------------------------------------------
+    // format
     //-----------------------------------------------------------------------
-    @DataProvider(name="print")
-    Object[][] data_format() {
+    @DataProvider(name="formatWithZoneWithChronology")
+    Object[][] data_format_withZone_withChronology() {
+        YearMonth ym = YearMonth.of(2008, 6);
         LocalDate ld = LocalDate.of(2008, 6, 30);
         LocalTime lt = LocalTime.of(11, 30);
         LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 11, 30);
         OffsetTime ot = OffsetTime.of(LocalTime.of(11, 30), OFFSET_PONE);
         OffsetDateTime odt = OffsetDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30), OFFSET_PONE);
         ZonedDateTime zdt = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30), ZONE_PARIS);
+        ChronoZonedDateTime<ThaiBuddhistDate> thaiZdt = ThaiBuddhistChronology.INSTANCE.zonedDateTime(zdt);
         Instant instant = Instant.ofEpochSecond(3600);
         return new Object[][] {
-                {null, null, ld, "2008::"},
-                {null, null, lt, ":11:"},
-                {null, null, ldt, "2008:11:"},
-                {null, null, ot, ":11:+01:00"},
-                {null, null, odt, "2008:11:+01:00"},
-                {null, null, zdt, "2008:11:+02:00Europe/Paris"},
-                {null, null, instant, "::"},
-
-                {null, ZONE_PARIS, ld, "2008::"},
-                {null, ZONE_PARIS, lt, ":11:"},
-                {null, ZONE_PARIS, ldt, "2008:11:"},
-                {null, ZONE_PARIS, ot, ":11:+01:00"},
-                {null, ZONE_PARIS, odt, "2008:12:+02:00Europe/Paris"},
-                {null, ZONE_PARIS, zdt, "2008:11:+02:00Europe/Paris"},
-                {null, ZONE_PARIS, instant, "1970:02:+01:00Europe/Paris"},
-
-                {null, OFFSET_PTHREE, ld, "2008::"},
-                {null, OFFSET_PTHREE, lt, ":11:"},
-                {null, OFFSET_PTHREE, ldt, "2008:11:"},
-                {null, OFFSET_PTHREE, ot, ":11:+01:00"},
-                {null, OFFSET_PTHREE, odt, "2008:13:+03:00"},
-                {null, OFFSET_PTHREE, zdt, "2008:12:+03:00"},
-                {null, OFFSET_PTHREE, instant, "1970:04:+03:00"},
-
-                {ThaiBuddhistChronology.INSTANCE, null, ld, "2551::"},
-                {ThaiBuddhistChronology.INSTANCE, null, lt, ":11:"},
-                {ThaiBuddhistChronology.INSTANCE, null, ldt, "2551:11:"},
-                {ThaiBuddhistChronology.INSTANCE, null, ot, ":11:+01:00"},
-                {ThaiBuddhistChronology.INSTANCE, null, odt, "2551:11:+01:00"},
-                {ThaiBuddhistChronology.INSTANCE, null, zdt, "2551:11:+02:00Europe/Paris"},
-                {ThaiBuddhistChronology.INSTANCE, null, instant, "::"},
-
-                {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, ld, "2551::"},
-                {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, lt, ":11:"},
-                {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, ldt, "2551:11:"},
-                {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, ot, ":11:+01:00"},
-                {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, odt, "2551:12:+02:00Europe/Paris"},
-                {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, zdt, "2551:11:+02:00Europe/Paris"},
-                {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, instant, "1970:02:+01:00Europe/Paris"},
+                {null, null, DayOfWeek.MONDAY, "::::"},
+                {null, null, ym, "2008::::ISO"},
+                {null, null, ld, "2008::::ISO"},
+                {null, null, lt, ":11:::"},
+                {null, null, ldt, "2008:11:::ISO"},
+                {null, null, ot, ":11:+01:00::"},
+                {null, null, odt, "2008:11:+01:00::ISO"},
+                {null, null, zdt, "2008:11:+02:00:Europe/Paris:ISO"},
+                {null, null, instant, "::::"},
+
+                {IsoChronology.INSTANCE, null, DayOfWeek.MONDAY, "::::ISO"},
+                {IsoChronology.INSTANCE, null, ym, "2008::::ISO"},
+                {IsoChronology.INSTANCE, null, ld, "2008::::ISO"},
+                {IsoChronology.INSTANCE, null, lt, ":11:::ISO"},
+                {IsoChronology.INSTANCE, null, ldt, "2008:11:::ISO"},
+                {IsoChronology.INSTANCE, null, ot, ":11:+01:00::ISO"},
+                {IsoChronology.INSTANCE, null, odt, "2008:11:+01:00::ISO"},
+                {IsoChronology.INSTANCE, null, zdt, "2008:11:+02:00:Europe/Paris:ISO"},
+                {IsoChronology.INSTANCE, null, instant, "::::ISO"},
+
+                {null, ZONE_PARIS, DayOfWeek.MONDAY, ":::Europe/Paris:"},
+                {null, ZONE_PARIS, ym, "2008:::Europe/Paris:ISO"},
+                {null, ZONE_PARIS, ld, "2008:::Europe/Paris:ISO"},
+                {null, ZONE_PARIS, lt, ":11::Europe/Paris:"},
+                {null, ZONE_PARIS, ldt, "2008:11::Europe/Paris:ISO"},
+                {null, ZONE_PARIS, ot, ":11:+01:00:Europe/Paris:"},
+                {null, ZONE_PARIS, odt, "2008:12:+02:00:Europe/Paris:ISO"},
+                {null, ZONE_PARIS, zdt, "2008:11:+02:00:Europe/Paris:ISO"},
+                {null, ZONE_PARIS, instant, "1970:02:+01:00:Europe/Paris:ISO"},
+
+                {null, OFFSET_PTHREE, DayOfWeek.MONDAY, ":::+03:00:"},
+                {null, OFFSET_PTHREE, ym, "2008:::+03:00:ISO"},
+                {null, OFFSET_PTHREE, ld, "2008:::+03:00:ISO"},
+                {null, OFFSET_PTHREE, lt, ":11::+03:00:"},
+                {null, OFFSET_PTHREE, ldt, "2008:11::+03:00:ISO"},
+                {null, OFFSET_PTHREE, ot, null},  // offset and zone clash
+                {null, OFFSET_PTHREE, odt, "2008:13:+03:00:+03:00:ISO"},
+                {null, OFFSET_PTHREE, zdt, "2008:12:+03:00:+03:00:ISO"},
+                {null, OFFSET_PTHREE, instant, "1970:04:+03:00:+03:00:ISO"},
+
+                {ThaiBuddhistChronology.INSTANCE, null, DayOfWeek.MONDAY, null},  // not a complete date
+                {ThaiBuddhistChronology.INSTANCE, null, ym, null},  // not a complete date
+                {ThaiBuddhistChronology.INSTANCE, null, ld, "2551::::ThaiBuddhist"},
+                {ThaiBuddhistChronology.INSTANCE, null, lt, ":11:::ThaiBuddhist"},
+                {ThaiBuddhistChronology.INSTANCE, null, ldt, "2551:11:::ThaiBuddhist"},
+                {ThaiBuddhistChronology.INSTANCE, null, ot, ":11:+01:00::ThaiBuddhist"},
+                {ThaiBuddhistChronology.INSTANCE, null, odt, "2551:11:+01:00::ThaiBuddhist"},
+                {ThaiBuddhistChronology.INSTANCE, null, zdt, "2551:11:+02:00:Europe/Paris:ThaiBuddhist"},
+                {ThaiBuddhistChronology.INSTANCE, null, instant, "::::ThaiBuddhist"},
+
+                {ThaiBuddhistChronology.INSTANCE, null, DayOfWeek.MONDAY, null},  // not a complete date
+                {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, ym, null},  // not a complete date
+                {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, ld, "2551:::Europe/Paris:ThaiBuddhist"},
+                {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, lt, ":11::Europe/Paris:ThaiBuddhist"},
+                {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, ldt, "2551:11::Europe/Paris:ThaiBuddhist"},
+                {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, ot, ":11:+01:00:Europe/Paris:ThaiBuddhist"},
+                {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, odt, "2551:12:+02:00:Europe/Paris:ThaiBuddhist"},
+                {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, zdt, "2551:11:+02:00:Europe/Paris:ThaiBuddhist"},
+                {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, instant, "2513:02:+01:00:Europe/Paris:ThaiBuddhist"},
+
+                {null, ZONE_PARIS, thaiZdt, "2551:11:+02:00:Europe/Paris:ThaiBuddhist"},
+                {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, thaiZdt, "2551:11:+02:00:Europe/Paris:ThaiBuddhist"},
+                {IsoChronology.INSTANCE, ZONE_PARIS, thaiZdt, "2008:11:+02:00:Europe/Paris:ISO"},
         };
     }
 
-    @Test(dataProvider="print")
-    public void test_print_Temporal(Chronology overrideChrono, ZoneId overrideZone, Temporal temporal, String expected) {
+    @Test(dataProvider="formatWithZoneWithChronology")
+    public void test_format_withZone_withChronology(Chronology overrideChrono, ZoneId overrideZone, TemporalAccessor temporal, String expected) {
         DateTimeFormatter test = new DateTimeFormatterBuilder()
                 .optionalStart().appendValue(YEAR, 4).optionalEnd()
                 .appendLiteral(':').optionalStart().appendValue(HOUR_OF_DAY, 2).optionalEnd()
-                .appendLiteral(':').optionalStart().appendOffsetId().optionalStart().appendZoneRegionId().optionalEnd().optionalEnd()
+                .appendLiteral(':').optionalStart().appendOffsetId().optionalEnd()
+                .appendLiteral(':').optionalStart().appendZoneId().optionalEnd()
+                .appendLiteral(':').optionalStart().appendChronologyId().optionalEnd()
                 .toFormatter(Locale.ENGLISH)
                 .withChronology(overrideChrono).withZone(overrideZone);
+        if (expected != null) {
         String result = test.format(temporal);
         assertEquals(result, expected);
+        } else {
+            try {
+                test.format(temporal);
+                fail("Formatting should have failed");
+            } catch (DateTimeException ex) {
+                // expected
+            }
+        }
     }
 
     @Test
-    public void test_print_Temporal_simple() throws Exception {
+    public void test_format_withChronology_nonChronoFieldMapLink() {
+        TemporalAccessor temporal = new TemporalAccessor() {
+            @Override
+            public boolean isSupported(TemporalField field) {
+                return field == IsoFields.WEEK_BASED_YEAR;
+            }
+            @Override
+            public long getLong(TemporalField field) {
+                if (field == IsoFields.WEEK_BASED_YEAR) {
+                    return 2345;
+                }
+                throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+            }
+        };
+        DateTimeFormatter test = new DateTimeFormatterBuilder()
+                .appendValue(IsoFields.WEEK_BASED_YEAR, 4)
+                .toFormatter(Locale.ENGLISH)
+                .withChronology(IsoChronology.INSTANCE);
+        String result = test.format(temporal);
+        assertEquals(result, "2345");
+    }
+
+    //-----------------------------------------------------------------------
+    @Test
+    public void test_format_TemporalAccessor_simple() {
         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
         String result = test.format(LocalDate.of(2008, 6, 30));
         assertEquals(result, "ONE30");
     }
 
-    @Test(expectedExceptions=DateTimeException.class)
-    public void test_print_Temporal_noSuchField() throws Exception {
+    @Test(expectedExceptions = DateTimeException.class)
+    public void test_format_TemporalAccessor_noSuchField() {
         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
         test.format(LocalTime.of(11, 30));
     }
 
-    @Test(expectedExceptions=NullPointerException.class)
-    public void test_print_Temporal_null() throws Exception {
+    @Test(expectedExceptions = NullPointerException.class)
+    public void test_format_TemporalAccessor_null() {
         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
         test.format((TemporalAccessor) null);
     }
 
     //-----------------------------------------------------------------------