1 /*
   2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * This file is available under and governed by the GNU General Public
  26  * License version 2 only, as published by the Free Software Foundation.
  27  * However, the following notice accompanied the original version of this
  28  * file:
  29  *
  30  * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos
  31  *
  32  * All rights reserved.
  33  *
  34  * Redistribution and use in source and binary forms, with or without
  35  * modification, are permitted provided that the following conditions are met:
  36  *
  37  *  * Redistributions of source code must retain the above copyright notice,
  38  *    this list of conditions and the following disclaimer.
  39  *
  40  *  * Redistributions in binary form must reproduce the above copyright notice,
  41  *    this list of conditions and the following disclaimer in the documentation
  42  *    and/or other materials provided with the distribution.
  43  *
  44  *  * Neither the name of JSR-310 nor the names of its contributors
  45  *    may be used to endorse or promote products derived from this software
  46  *    without specific prior written permission.
  47  *
  48  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  49  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  50  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  51  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  52  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  53  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  54  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  55  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  56  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  57  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  58  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  59  */
  60 package tck.java.time.format;
  61 
  62 import static java.time.temporal.ChronoField.DAY_OF_MONTH;
  63 import static java.time.temporal.ChronoField.HOUR_OF_DAY;
  64 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
  65 import static java.time.temporal.ChronoField.YEAR;
  66 import static org.testng.Assert.assertEquals;
  67 import static org.testng.Assert.assertNull;
  68 import static org.testng.Assert.assertTrue;
  69 import static org.testng.Assert.fail;
  70 
  71 import java.text.Format;
  72 import java.text.ParseException;
  73 import java.text.ParsePosition;
  74 import java.util.Locale;
  75 
  76 import java.time.DateTimeException;
  77 import java.time.Instant;
  78 import java.time.LocalDate;
  79 import java.time.LocalDateTime;
  80 import java.time.LocalTime;
  81 import java.time.ZoneId;
  82 import java.time.ZoneOffset;
  83 import java.time.ZonedDateTime;
  84 import java.time.chrono.ThaiBuddhistChronology;
  85 import java.time.format.DateTimeFormatSymbols;
  86 import java.time.format.DateTimeFormatter;
  87 import java.time.format.DateTimeFormatterBuilder;
  88 import java.time.format.DateTimeParseException;
  89 import java.time.format.SignStyle;
  90 import java.time.chrono.Chronology;
  91 import java.time.chrono.IsoChronology;
  92 import java.time.OffsetDateTime;
  93 import java.time.OffsetTime;
  94 import java.time.temporal.Temporal;
  95 import java.time.temporal.TemporalAccessor;
  96 import java.time.temporal.TemporalQuery;
  97 
  98 import org.testng.annotations.BeforeMethod;
  99 import org.testng.annotations.DataProvider;
 100 import org.testng.annotations.Test;
 101 
 102 /**
 103  * Test DateTimeFormatter.
 104  */
 105 @Test(groups={"tck"})
 106 public class TCKDateTimeFormatter {
 107 
 108     private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1);
 109     private static final ZoneOffset OFFSET_PTHREE = ZoneOffset.ofHours(3);
 110     private static final ZoneId ZONE_PARIS = ZoneId.of("Europe/Paris");
 111 
 112     private static final DateTimeFormatter BASIC_FORMATTER = DateTimeFormatter.ofPattern("'ONE'd");
 113     private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("'ONE'yyyy MM dd");
 114 
 115     private DateTimeFormatter fmt;
 116 
 117     @BeforeMethod
 118     public void setUp() {
 119         fmt = new DateTimeFormatterBuilder().appendLiteral("ONE")
 120                                             .appendValue(DAY_OF_MONTH, 1, 2, SignStyle.NOT_NEGATIVE)
 121                                             .toFormatter();
 122     }
 123 
 124     //-----------------------------------------------------------------------
 125     @Test
 126     public void test_withLocale() {
 127         DateTimeFormatter base = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 128         DateTimeFormatter test = base.withLocale(Locale.GERMAN);
 129         assertEquals(test.getLocale(), Locale.GERMAN);
 130     }
 131 
 132     @Test(expectedExceptions=NullPointerException.class)
 133     public void test_withLocale_null() {
 134         DateTimeFormatter base = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 135         base.withLocale((Locale) null);
 136     }
 137 
 138     //-----------------------------------------------------------------------
 139     @Test
 140     public void test_withChronology() {
 141         DateTimeFormatter test = fmt;
 142         assertEquals(test.getChronology(), null);
 143         test = test.withChronology(IsoChronology.INSTANCE);
 144         assertEquals(test.getChronology(), IsoChronology.INSTANCE);
 145         test = test.withChronology(null);
 146         assertEquals(test.getChronology(), null);
 147     }
 148 
 149     //-----------------------------------------------------------------------
 150     @Test
 151     public void test_withZone() {
 152         DateTimeFormatter test = fmt;
 153         assertEquals(test.getZone(), null);
 154         test = test.withZone(ZoneId.of("Europe/Paris"));
 155         assertEquals(test.getZone(), ZoneId.of("Europe/Paris"));
 156         test = test.withZone(ZoneOffset.UTC);
 157         assertEquals(test.getZone(), ZoneOffset.UTC);
 158         test = test.withZone(null);
 159         assertEquals(test.getZone(), null);
 160     }
 161 
 162     //-----------------------------------------------------------------------
 163     // print
 164     //-----------------------------------------------------------------------
 165     @DataProvider(name="print")
 166     Object[][] data_format() {
 167         LocalDate ld = LocalDate.of(2008, 6, 30);
 168         LocalTime lt = LocalTime.of(11, 30);
 169         LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 11, 30);
 170         OffsetTime ot = OffsetTime.of(LocalTime.of(11, 30), OFFSET_PONE);
 171         OffsetDateTime odt = OffsetDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30), OFFSET_PONE);
 172         ZonedDateTime zdt = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30), ZONE_PARIS);
 173         Instant instant = Instant.ofEpochSecond(3600);
 174         return new Object[][] {
 175                 {null, null, ld, "2008::"},
 176                 {null, null, lt, ":11:"},
 177                 {null, null, ldt, "2008:11:"},
 178                 {null, null, ot, ":11:+01:00"},
 179                 {null, null, odt, "2008:11:+01:00"},
 180                 {null, null, zdt, "2008:11:+02:00Europe/Paris"},
 181                 {null, null, instant, "::"},
 182 
 183                 {null, ZONE_PARIS, ld, "2008::"},
 184                 {null, ZONE_PARIS, lt, ":11:"},
 185                 {null, ZONE_PARIS, ldt, "2008:11:"},
 186                 {null, ZONE_PARIS, ot, ":11:+01:00"},
 187                 {null, ZONE_PARIS, odt, "2008:12:+02:00Europe/Paris"},
 188                 {null, ZONE_PARIS, zdt, "2008:11:+02:00Europe/Paris"},
 189                 {null, ZONE_PARIS, instant, "1970:02:+01:00Europe/Paris"},
 190 
 191                 {null, OFFSET_PTHREE, ld, "2008::"},
 192                 {null, OFFSET_PTHREE, lt, ":11:"},
 193                 {null, OFFSET_PTHREE, ldt, "2008:11:"},
 194                 {null, OFFSET_PTHREE, ot, ":11:+01:00"},
 195                 {null, OFFSET_PTHREE, odt, "2008:13:+03:00"},
 196                 {null, OFFSET_PTHREE, zdt, "2008:12:+03:00"},
 197                 {null, OFFSET_PTHREE, instant, "1970:04:+03:00"},
 198 
 199                 {ThaiBuddhistChronology.INSTANCE, null, ld, "2551::"},
 200                 {ThaiBuddhistChronology.INSTANCE, null, lt, ":11:"},
 201                 {ThaiBuddhistChronology.INSTANCE, null, ldt, "2551:11:"},
 202                 {ThaiBuddhistChronology.INSTANCE, null, ot, ":11:+01:00"},
 203                 {ThaiBuddhistChronology.INSTANCE, null, odt, "2551:11:+01:00"},
 204                 {ThaiBuddhistChronology.INSTANCE, null, zdt, "2551:11:+02:00Europe/Paris"},
 205                 {ThaiBuddhistChronology.INSTANCE, null, instant, "::"},
 206 
 207                 {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, ld, "2551::"},
 208                 {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, lt, ":11:"},
 209                 {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, ldt, "2551:11:"},
 210                 {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, ot, ":11:+01:00"},
 211                 {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, odt, "2551:12:+02:00Europe/Paris"},
 212                 {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, zdt, "2551:11:+02:00Europe/Paris"},
 213                 {ThaiBuddhistChronology.INSTANCE, ZONE_PARIS, instant, "1970:02:+01:00Europe/Paris"},
 214         };
 215     }
 216 
 217     @Test(dataProvider="print")
 218     public void test_print_Temporal(Chronology overrideChrono, ZoneId overrideZone, Temporal temporal, String expected) {
 219         DateTimeFormatter test = new DateTimeFormatterBuilder()
 220                 .optionalStart().appendValue(YEAR, 4).optionalEnd()
 221                 .appendLiteral(':').optionalStart().appendValue(HOUR_OF_DAY, 2).optionalEnd()
 222                 .appendLiteral(':').optionalStart().appendOffsetId().optionalStart().appendZoneRegionId().optionalEnd().optionalEnd()
 223                 .toFormatter(Locale.ENGLISH)
 224                 .withChronology(overrideChrono).withZone(overrideZone);
 225         String result = test.format(temporal);
 226         assertEquals(result, expected);
 227     }
 228 
 229     @Test
 230     public void test_print_Temporal_simple() throws Exception {
 231         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 232         String result = test.format(LocalDate.of(2008, 6, 30));
 233         assertEquals(result, "ONE30");
 234     }
 235 
 236     @Test(expectedExceptions=DateTimeException.class)
 237     public void test_print_Temporal_noSuchField() throws Exception {
 238         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 239         test.format(LocalTime.of(11, 30));
 240     }
 241 
 242     @Test(expectedExceptions=NullPointerException.class)
 243     public void test_print_Temporal_null() throws Exception {
 244         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 245         test.format((TemporalAccessor) null);
 246     }
 247 
 248     //-----------------------------------------------------------------------
 249     @Test
 250     public void test_print_TemporalAppendable() throws Exception {
 251         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 252         StringBuilder buf = new StringBuilder();
 253         test.formatTo(LocalDate.of(2008, 6, 30), buf);
 254         assertEquals(buf.toString(), "ONE30");
 255     }
 256 
 257     @Test(expectedExceptions=DateTimeException.class)
 258     public void test_print_TemporalAppendable_noSuchField() throws Exception {
 259         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 260         StringBuilder buf = new StringBuilder();
 261         test.formatTo(LocalTime.of(11, 30), buf);
 262     }
 263 
 264     @Test(expectedExceptions=NullPointerException.class)
 265     public void test_print_TemporalAppendable_nullTemporal() throws Exception {
 266         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 267         StringBuilder buf = new StringBuilder();
 268         test.formatTo((TemporalAccessor) null, buf);
 269     }
 270 
 271     @Test(expectedExceptions=NullPointerException.class)
 272     public void test_print_TemporalAppendable_nullAppendable() throws Exception {
 273         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 274         test.formatTo(LocalDate.of(2008, 6, 30), (Appendable) null);
 275     }
 276 
 277     //-----------------------------------------------------------------------
 278     // parse(CharSequence)
 279     //-----------------------------------------------------------------------
 280     @Test
 281     public void test_parse_CharSequence() {
 282         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 283         TemporalAccessor result = test.parse("ONE30");
 284         assertEquals(result.isSupported(DAY_OF_MONTH), true);
 285         assertEquals(result.getLong(DAY_OF_MONTH), 30L);
 286         assertEquals(result.isSupported(HOUR_OF_DAY), false);
 287     }
 288 
 289     @Test
 290     public void test_parse_CharSequence_resolved() {
 291         DateTimeFormatter test = DateTimeFormatter.ISO_DATE;
 292         TemporalAccessor result = test.parse("2012-06-30");
 293         assertEquals(result.isSupported(YEAR), true);
 294         assertEquals(result.isSupported(MONTH_OF_YEAR), true);
 295         assertEquals(result.isSupported(DAY_OF_MONTH), true);
 296         assertEquals(result.isSupported(HOUR_OF_DAY), false);
 297         assertEquals(result.getLong(YEAR), 2012L);
 298         assertEquals(result.getLong(MONTH_OF_YEAR), 6L);
 299         assertEquals(result.getLong(DAY_OF_MONTH), 30L);
 300         assertEquals(result.query(LocalDate::from), LocalDate.of(2012, 6, 30));
 301     }
 302 
 303     @Test(expectedExceptions=NullPointerException.class)
 304     public void test_parse_CharSequence_null() {
 305         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 306         test.parse((String) null);
 307     }
 308 
 309     //-----------------------------------------------------------------------
 310     // parse(CharSequence)
 311     //-----------------------------------------------------------------------
 312     @Test
 313     public void test_parse_CharSequence_ParsePosition() {
 314         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 315         ParsePosition pos = new ParsePosition(3);
 316         TemporalAccessor result = test.parse("XXXONE30XXX", pos);
 317         assertEquals(pos.getIndex(), 8);
 318         assertEquals(pos.getErrorIndex(), -1);
 319         assertEquals(result.isSupported(DAY_OF_MONTH), true);
 320         assertEquals(result.getLong(DAY_OF_MONTH), 30L);
 321         assertEquals(result.isSupported(HOUR_OF_DAY), false);
 322     }
 323 
 324     @Test
 325     public void test_parse_CharSequence_ParsePosition_resolved() {
 326         DateTimeFormatter test = DateTimeFormatter.ISO_DATE;
 327         ParsePosition pos = new ParsePosition(3);
 328         TemporalAccessor result = test.parse("XXX2012-06-30XXX", pos);
 329         assertEquals(pos.getIndex(), 13);
 330         assertEquals(pos.getErrorIndex(), -1);
 331         assertEquals(result.isSupported(YEAR), true);
 332         assertEquals(result.isSupported(MONTH_OF_YEAR), true);
 333         assertEquals(result.isSupported(DAY_OF_MONTH), true);
 334         assertEquals(result.isSupported(HOUR_OF_DAY), false);
 335         assertEquals(result.getLong(YEAR), 2012L);
 336         assertEquals(result.getLong(MONTH_OF_YEAR), 6L);
 337         assertEquals(result.getLong(DAY_OF_MONTH), 30L);
 338         assertEquals(result.query(LocalDate::from), LocalDate.of(2012, 6, 30));
 339     }
 340 
 341     @Test(expectedExceptions=DateTimeParseException.class)
 342     public void test_parse_CharSequence_ParsePosition_parseError() {
 343         DateTimeFormatter test = DateTimeFormatter.ISO_DATE;
 344         ParsePosition pos = new ParsePosition(3);
 345         try {
 346             test.parse("XXX2012XXX", pos);
 347             fail();
 348         } catch (DateTimeParseException ex) {
 349             assertEquals(ex.getErrorIndex(), 7);
 350             throw ex;
 351         }
 352     }
 353 
 354     @Test(expectedExceptions=IndexOutOfBoundsException.class)
 355     public void test_parse_CharSequence_ParsePosition_indexTooBig() {
 356         DateTimeFormatter test = DateTimeFormatter.ISO_DATE;
 357         test.parse("Text", new ParsePosition(5));
 358     }
 359 
 360     @Test(expectedExceptions=NullPointerException.class)
 361     public void test_parse_CharSequence_ParsePosition_nullText() {
 362         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 363         test.parse((CharSequence) null, new ParsePosition(0));
 364     }
 365 
 366     @Test(expectedExceptions=NullPointerException.class)
 367     public void test_parse_CharSequence_ParsePosition_nullParsePosition() {
 368         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 369         test.parse("Text", (ParsePosition) null);
 370     }
 371 
 372     //-----------------------------------------------------------------------
 373     // parse(Query)
 374     //-----------------------------------------------------------------------
 375     @Test
 376     public void test_parse_Query_String() throws Exception {
 377         LocalDate result = DATE_FORMATTER.parse("ONE2012 07 27", LocalDate::from);
 378         assertEquals(result, LocalDate.of(2012, 7, 27));
 379     }
 380 
 381     @Test
 382     public void test_parse_Query_CharSequence() throws Exception {
 383         LocalDate result = DATE_FORMATTER.parse(new StringBuilder("ONE2012 07 27"), LocalDate::from);
 384         assertEquals(result, LocalDate.of(2012, 7, 27));
 385     }
 386 
 387     @Test(expectedExceptions=DateTimeParseException.class)
 388     public void test_parse_Query_String_parseError() throws Exception {
 389         try {
 390             DATE_FORMATTER.parse("ONE2012 07 XX", LocalDate::from);
 391         } catch (DateTimeParseException ex) {
 392             assertEquals(ex.getMessage().contains("could not be parsed"), true);
 393             assertEquals(ex.getMessage().contains("ONE2012 07 XX"), true);
 394             assertEquals(ex.getParsedString(), "ONE2012 07 XX");
 395             assertEquals(ex.getErrorIndex(), 11);
 396             throw ex;
 397         }
 398     }
 399 
 400     @Test(expectedExceptions=DateTimeParseException.class)
 401     public void test_parse_Query_String_parseErrorLongText() throws Exception {
 402         try {
 403             DATE_FORMATTER.parse("ONEXXX67890123456789012345678901234567890123456789012345678901234567890123456789", LocalDate::from);
 404         } catch (DateTimeParseException ex) {
 405             assertEquals(ex.getMessage().contains("could not be parsed"), true);
 406             assertEquals(ex.getMessage().contains("ONEXXX6789012345678901234567890123456789012345678901234567890123..."), true);
 407             assertEquals(ex.getParsedString(), "ONEXXX67890123456789012345678901234567890123456789012345678901234567890123456789");
 408             assertEquals(ex.getErrorIndex(), 3);
 409             throw ex;
 410         }
 411     }
 412 
 413     @Test(expectedExceptions=DateTimeParseException.class)
 414     public void test_parse_Query_String_parseIncomplete() throws Exception {
 415         try {
 416             DATE_FORMATTER.parse("ONE2012 07 27SomethingElse", LocalDate::from);
 417         } catch (DateTimeParseException ex) {
 418             assertEquals(ex.getMessage().contains("could not be parsed"), true);
 419             assertEquals(ex.getMessage().contains("ONE2012 07 27SomethingElse"), true);
 420             assertEquals(ex.getParsedString(), "ONE2012 07 27SomethingElse");
 421             assertEquals(ex.getErrorIndex(), 13);
 422             throw ex;
 423         }
 424     }
 425 
 426     @Test(expectedExceptions=NullPointerException.class)
 427     public void test_parse_Query_String_nullText() throws Exception {
 428         DATE_FORMATTER.parse((String) null, LocalDate::from);
 429     }
 430 
 431     @Test(expectedExceptions=NullPointerException.class)
 432     public void test_parse_Query_String_nullRule() throws Exception {
 433         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 434         test.parse("30", (TemporalQuery<?>) null);
 435     }
 436 
 437     //-----------------------------------------------------------------------
 438     @Test
 439     public void test_parseBest_firstOption() throws Exception {
 440         DateTimeFormatter test = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm[XXX]");
 441         TemporalAccessor result = test.parseBest("2011-06-30 12:30+03:00", ZonedDateTime::from, LocalDateTime::from);
 442         LocalDateTime ldt = LocalDateTime.of(2011, 6, 30, 12, 30);
 443         assertEquals(result, ZonedDateTime.of(ldt, ZoneOffset.ofHours(3)));
 444     }
 445 
 446     @Test
 447     public void test_parseBest_secondOption() throws Exception {
 448         DateTimeFormatter test = DateTimeFormatter.ofPattern("yyyy-MM-dd[ HH:mm[XXX]]");
 449         TemporalAccessor result = test.parseBest("2011-06-30", ZonedDateTime::from, LocalDate::from);
 450         assertEquals(result, LocalDate.of(2011, 6, 30));
 451     }
 452 
 453     @Test(expectedExceptions=DateTimeParseException.class)
 454     public void test_parseBest_String_parseError() throws Exception {
 455         DateTimeFormatter test = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm[XXX]");
 456         try {
 457             test.parseBest("2011-06-XX", ZonedDateTime::from, LocalDateTime::from);
 458         } catch (DateTimeParseException ex) {
 459             assertEquals(ex.getMessage().contains("could not be parsed"), true);
 460             assertEquals(ex.getMessage().contains("XX"), true);
 461             assertEquals(ex.getParsedString(), "2011-06-XX");
 462             assertEquals(ex.getErrorIndex(), 8);
 463             throw ex;
 464         }
 465     }
 466 
 467     @Test(expectedExceptions=DateTimeParseException.class)
 468     public void test_parseBest_String_parseErrorLongText() throws Exception {
 469         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 470         try {
 471             test.parseBest("ONEXXX67890123456789012345678901234567890123456789012345678901234567890123456789", ZonedDateTime::from, LocalDate::from);
 472         } catch (DateTimeParseException ex) {
 473             assertEquals(ex.getMessage().contains("could not be parsed"), true);
 474             assertEquals(ex.getMessage().contains("ONEXXX6789012345678901234567890123456789012345678901234567890123..."), true);
 475             assertEquals(ex.getParsedString(), "ONEXXX67890123456789012345678901234567890123456789012345678901234567890123456789");
 476             assertEquals(ex.getErrorIndex(), 3);
 477             throw ex;
 478         }
 479     }
 480 
 481     @Test(expectedExceptions=DateTimeParseException.class)
 482     public void test_parseBest_String_parseIncomplete() throws Exception {
 483         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 484         try {
 485             test.parseBest("ONE30SomethingElse", ZonedDateTime::from, LocalDate::from);
 486         } catch (DateTimeParseException ex) {
 487             assertEquals(ex.getMessage().contains("could not be parsed"), true);
 488             assertEquals(ex.getMessage().contains("ONE30SomethingElse"), true);
 489             assertEquals(ex.getParsedString(), "ONE30SomethingElse");
 490             assertEquals(ex.getErrorIndex(), 5);
 491             throw ex;
 492         }
 493     }
 494 
 495     @Test(expectedExceptions=NullPointerException.class)
 496     public void test_parseBest_String_nullText() throws Exception {
 497         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 498         test.parseBest((String) null, ZonedDateTime::from, LocalDate::from);
 499     }
 500 
 501     @Test(expectedExceptions=NullPointerException.class)
 502     public void test_parseBest_String_nullRules() throws Exception {
 503         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 504         test.parseBest("30", (TemporalQuery<?>[]) null);
 505     }
 506 
 507     @Test(expectedExceptions=IllegalArgumentException.class)
 508     public void test_parseBest_String_zeroRules() throws Exception {
 509         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 510         test.parseBest("30", new TemporalQuery<?>[0]);
 511     }
 512 
 513     @Test(expectedExceptions=IllegalArgumentException.class)
 514     public void test_parseBest_String_oneRule() throws Exception {
 515         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 516         test.parseBest("30", LocalDate::from);
 517     }
 518 
 519     //-----------------------------------------------------------------------
 520     @Test
 521     public void test_parseUnresolved_StringParsePosition() {
 522         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 523         ParsePosition pos = new ParsePosition(0);
 524         TemporalAccessor result = test.parseUnresolved("ONE30XXX", pos);
 525         assertEquals(pos.getIndex(), 5);
 526         assertEquals(pos.getErrorIndex(), -1);
 527         assertEquals(result.getLong(DAY_OF_MONTH), 30L);
 528     }
 529 
 530     @Test
 531     public void test_parseUnresolved_StringParsePosition_parseError() {
 532         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 533         ParsePosition pos = new ParsePosition(0);
 534         TemporalAccessor result = test.parseUnresolved("ONEXXX", pos);
 535         assertEquals(pos.getIndex(), 0);
 536         assertEquals(pos.getErrorIndex(), 3);
 537         assertEquals(result, null);
 538     }
 539 
 540     @Test
 541     public void test_parseUnresolved_StringParsePosition_duplicateFieldSameValue() {
 542         DateTimeFormatter test = new DateTimeFormatterBuilder()
 543                 .appendValue(MONTH_OF_YEAR).appendLiteral('-').appendValue(MONTH_OF_YEAR).toFormatter();
 544         ParsePosition pos = new ParsePosition(3);
 545         TemporalAccessor result = test.parseUnresolved("XXX6-6", pos);
 546         assertEquals(pos.getIndex(), 6);
 547         assertEquals(pos.getErrorIndex(), -1);
 548         assertEquals(result.getLong(MONTH_OF_YEAR), 6);
 549     }
 550 
 551     @Test
 552     public void test_parseUnresolved_StringParsePosition_duplicateFieldDifferentValue() {
 553         DateTimeFormatter test = new DateTimeFormatterBuilder()
 554                 .appendValue(MONTH_OF_YEAR).appendLiteral('-').appendValue(MONTH_OF_YEAR).toFormatter();
 555         ParsePosition pos = new ParsePosition(3);
 556         TemporalAccessor result = test.parseUnresolved("XXX6-7", pos);
 557         assertEquals(pos.getIndex(), 3);
 558         assertEquals(pos.getErrorIndex(), 5);
 559         assertEquals(result, null);
 560     }
 561 
 562     @Test(expectedExceptions=NullPointerException.class)
 563     public void test_parseUnresolved_StringParsePosition_nullString() {
 564         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 565         ParsePosition pos = new ParsePosition(0);
 566         test.parseUnresolved((String) null, pos);
 567     }
 568 
 569     @Test(expectedExceptions=NullPointerException.class)
 570     public void test_parseUnresolved_StringParsePosition_nullParsePosition() {
 571         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 572         test.parseUnresolved("ONE30", (ParsePosition) null);
 573     }
 574 
 575     @Test(expectedExceptions=IndexOutOfBoundsException.class)
 576     public void test_parseUnresolved_StringParsePosition_invalidPosition() {
 577         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 578         ParsePosition pos = new ParsePosition(6);
 579         test.parseUnresolved("ONE30", pos);
 580     }
 581 
 582     //-----------------------------------------------------------------------
 583     //-----------------------------------------------------------------------
 584     @Test
 585     public void test_toFormat_format() throws Exception {
 586         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 587         Format format = test.toFormat();
 588         String result = format.format(LocalDate.of(2008, 6, 30));
 589         assertEquals(result, "ONE30");
 590     }
 591 
 592     @Test(expectedExceptions=NullPointerException.class)
 593     public void test_toFormat_format_null() throws Exception {
 594         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 595         Format format = test.toFormat();
 596         format.format(null);
 597     }
 598 
 599     @Test(expectedExceptions=IllegalArgumentException.class)
 600     public void test_toFormat_format_notTemporal() throws Exception {
 601         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 602         Format format = test.toFormat();
 603         format.format("Not a Temporal");
 604     }
 605 
 606     //-----------------------------------------------------------------------
 607     @Test
 608     public void test_toFormat_parseObject_String() throws Exception {
 609         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 610         Format format = test.toFormat();
 611         TemporalAccessor result = (TemporalAccessor) format.parseObject("ONE30");
 612         assertEquals(result.isSupported(DAY_OF_MONTH), true);
 613         assertEquals(result.getLong(DAY_OF_MONTH), 30L);
 614     }
 615 
 616     @Test(expectedExceptions=ParseException.class)
 617     public void test_toFormat_parseObject_String_parseError() throws Exception {
 618         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 619         Format format = test.toFormat();
 620         try {
 621             format.parseObject("ONEXXX");
 622         } catch (ParseException ex) {
 623             assertEquals(ex.getMessage().contains("ONEXXX"), true);
 624             assertEquals(ex.getErrorOffset(), 3);
 625             throw ex;
 626         }
 627     }
 628 
 629     @Test(expectedExceptions=ParseException.class)
 630     public void test_toFormat_parseObject_String_parseErrorLongText() throws Exception {
 631         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 632         Format format = test.toFormat();
 633         try {
 634             format.parseObject("ONEXXX67890123456789012345678901234567890123456789012345678901234567890123456789");
 635         } catch (DateTimeParseException ex) {
 636             assertEquals(ex.getMessage().contains("ONEXXX6789012345678901234567890123456789012345678901234567890123..."), true);
 637             assertEquals(ex.getParsedString(), "ONEXXX67890123456789012345678901234567890123456789012345678901234567890123456789");
 638             assertEquals(ex.getErrorIndex(), 3);
 639             throw ex;
 640         }
 641     }
 642 
 643     @Test(expectedExceptions=NullPointerException.class)
 644     public void test_toFormat_parseObject_String_null() throws Exception {
 645         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 646         Format format = test.toFormat();
 647         format.parseObject((String) null);
 648     }
 649 
 650     //-----------------------------------------------------------------------
 651     @Test
 652     public void test_toFormat_parseObject_StringParsePosition() throws Exception {
 653         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 654         Format format = test.toFormat();
 655         ParsePosition pos = new ParsePosition(0);
 656         TemporalAccessor result = (TemporalAccessor) format.parseObject("ONE30XXX", pos);
 657         assertEquals(pos.getIndex(), 5);
 658         assertEquals(pos.getErrorIndex(), -1);
 659         assertEquals(result.isSupported(DAY_OF_MONTH), true);
 660         assertEquals(result.getLong(DAY_OF_MONTH), 30L);
 661     }
 662 
 663     @Test
 664     public void test_toFormat_parseObject_StringParsePosition_parseError() throws Exception {
 665         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 666         Format format = test.toFormat();
 667         ParsePosition pos = new ParsePosition(0);
 668         TemporalAccessor result = (TemporalAccessor) format.parseObject("ONEXXX", pos);
 669         assertEquals(pos.getIndex(), 0);  // TODO: is this right?
 670         assertEquals(pos.getErrorIndex(), 3);
 671         assertEquals(result, null);
 672     }
 673 
 674     @Test(expectedExceptions=NullPointerException.class)
 675     public void test_toFormat_parseObject_StringParsePosition_nullString() throws Exception {
 676         // SimpleDateFormat has this behavior
 677         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 678         Format format = test.toFormat();
 679         ParsePosition pos = new ParsePosition(0);
 680         format.parseObject((String) null, pos);
 681     }
 682 
 683     @Test(expectedExceptions=NullPointerException.class)
 684     public void test_toFormat_parseObject_StringParsePosition_nullParsePosition() throws Exception {
 685         // SimpleDateFormat has this behavior
 686         DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 687         Format format = test.toFormat();
 688         format.parseObject("ONE30", (ParsePosition) null);
 689     }
 690 
 691     @Test
 692     public void test_toFormat_parseObject_StringParsePosition_invalidPosition_tooBig() throws Exception {
 693         // SimpleDateFormat has this behavior
 694         DateTimeFormatter dtf = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 695         ParsePosition pos = new ParsePosition(6);
 696         Format test = dtf.toFormat();
 697         assertNull(test.parseObject("ONE30", pos));
 698         assertTrue(pos.getErrorIndex() >= 0);
 699     }
 700 
 701     @Test
 702     public void test_toFormat_parseObject_StringParsePosition_invalidPosition_tooSmall() throws Exception {
 703         // SimpleDateFormat throws StringIndexOutOfBoundException
 704         DateTimeFormatter dtf = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD);
 705         ParsePosition pos = new ParsePosition(-1);
 706         Format test = dtf.toFormat();
 707         assertNull(test.parseObject("ONE30", pos));
 708         assertTrue(pos.getErrorIndex() >= 0);
 709     }
 710 
 711     //-----------------------------------------------------------------------
 712     @Test
 713     public void test_toFormat_Query_format() throws Exception {
 714         Format format = BASIC_FORMATTER.toFormat();
 715         String result = format.format(LocalDate.of(2008, 6, 30));
 716         assertEquals(result, "ONE30");
 717     }
 718 
 719     @Test
 720     public void test_toFormat_Query_parseObject_String() throws Exception {
 721         Format format = DATE_FORMATTER.toFormat(LocalDate::from);
 722         LocalDate result = (LocalDate) format.parseObject("ONE2012 07 27");
 723         assertEquals(result, LocalDate.of(2012, 7, 27));
 724     }
 725 
 726     @Test(expectedExceptions=ParseException.class)
 727     public void test_toFormat_parseObject_StringParsePosition_dateTimeError() throws Exception {
 728         Format format = DATE_FORMATTER.toFormat(LocalDate::from);
 729         format.parseObject("ONE2012 07 32");
 730     }
 731 
 732     @Test(expectedExceptions=NullPointerException.class)
 733     public void test_toFormat_Query() throws Exception {
 734         BASIC_FORMATTER.toFormat(null);
 735     }
 736 
 737 }