1 /*
   2  * Copyright (c) 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-2013, 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.ALIGNED_DAY_OF_WEEK_IN_MONTH;
  63 import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR;
  64 import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH;
  65 import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR;
  66 import static java.time.temporal.ChronoField.AMPM_OF_DAY;
  67 import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_AMPM;
  68 import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_DAY;
  69 import static java.time.temporal.ChronoField.DAY_OF_MONTH;
  70 import static java.time.temporal.ChronoField.DAY_OF_WEEK;
  71 import static java.time.temporal.ChronoField.DAY_OF_YEAR;
  72 import static java.time.temporal.ChronoField.EPOCH_DAY;
  73 import static java.time.temporal.ChronoField.ERA;
  74 import static java.time.temporal.ChronoField.HOUR_OF_AMPM;
  75 import static java.time.temporal.ChronoField.HOUR_OF_DAY;
  76 import static java.time.temporal.ChronoField.MICRO_OF_DAY;
  77 import static java.time.temporal.ChronoField.MICRO_OF_SECOND;
  78 import static java.time.temporal.ChronoField.MILLI_OF_DAY;
  79 import static java.time.temporal.ChronoField.MILLI_OF_SECOND;
  80 import static java.time.temporal.ChronoField.MINUTE_OF_DAY;
  81 import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
  82 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
  83 import static java.time.temporal.ChronoField.NANO_OF_DAY;
  84 import static java.time.temporal.ChronoField.NANO_OF_SECOND;
  85 import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
  86 import static java.time.temporal.ChronoField.SECOND_OF_DAY;
  87 import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
  88 import static java.time.temporal.ChronoField.YEAR;
  89 import static java.time.temporal.ChronoField.YEAR_OF_ERA;
  90 import static org.testng.Assert.assertEquals;
  91 
  92 import java.time.LocalDate;
  93 import java.time.LocalTime;
  94 import java.time.format.DateTimeFormatter;
  95 import java.time.format.DateTimeFormatterBuilder;
  96 import java.time.temporal.IsoFields;
  97 import java.time.temporal.TemporalAccessor;
  98 import java.time.temporal.TemporalField;
  99 import java.time.temporal.TemporalQuery;
 100 
 101 import org.testng.annotations.DataProvider;
 102 import org.testng.annotations.Test;
 103 
 104 /**
 105  * Test parse resolving.
 106  */
 107 @Test
 108 public class TCKDateTimeParseResolver {
 109     // TODO: tests with weird TenporalField implementations
 110     // TODO: tests with non-ISO chronologies
 111 
 112     //-----------------------------------------------------------------------
 113     @DataProvider(name="resolveOneNoChange")
 114     Object[][] data_resolveOneNoChange() {
 115         return new Object[][]{
 116                 {YEAR, 2012},
 117                 {MONTH_OF_YEAR, 8},
 118                 {DAY_OF_MONTH, 7},
 119                 {DAY_OF_YEAR, 6},
 120                 {DAY_OF_WEEK, 5},
 121         };
 122     }
 123 
 124     @Test(dataProvider="resolveOneNoChange")
 125     public void test_resolveOneNoChange(TemporalField field1, long value1) {
 126         String str = Long.toString(value1);
 127         DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(field1).toFormatter();
 128 
 129         TemporalAccessor accessor = f.parse(str);
 130         assertEquals(accessor.query(TemporalQuery.localDate()), null);
 131         assertEquals(accessor.query(TemporalQuery.localTime()), null);
 132         assertEquals(accessor.isSupported(field1), true);
 133         assertEquals(accessor.getLong(field1), value1);
 134     }
 135 
 136     //-----------------------------------------------------------------------
 137     @DataProvider(name="resolveTwoNoChange")
 138     Object[][] data_resolveTwoNoChange() {
 139         return new Object[][]{
 140                 {YEAR, 2012, MONTH_OF_YEAR, 5},
 141                 {YEAR, 2012, DAY_OF_MONTH, 5},
 142                 {YEAR, 2012, DAY_OF_WEEK, 5},
 143                 {YEAR, 2012, ALIGNED_WEEK_OF_YEAR, 5},
 144                 {YEAR, 2012, ALIGNED_WEEK_OF_MONTH, 5},
 145                 {YEAR, 2012, IsoFields.QUARTER_OF_YEAR, 3},
 146                 {YEAR, 2012, MINUTE_OF_HOUR, 5},
 147                 {YEAR, 2012, SECOND_OF_MINUTE, 5},
 148                 {YEAR, 2012, NANO_OF_SECOND, 5},
 149 
 150                 {MONTH_OF_YEAR, 5, DAY_OF_MONTH, 5},
 151                 {MONTH_OF_YEAR, 5, DAY_OF_WEEK, 5},
 152                 {MONTH_OF_YEAR, 5, ALIGNED_WEEK_OF_YEAR, 5},
 153                 {MONTH_OF_YEAR, 5, ALIGNED_WEEK_OF_MONTH, 5},
 154                 {MONTH_OF_YEAR, 3, IsoFields.QUARTER_OF_YEAR, 5},
 155                 {MONTH_OF_YEAR, 5, MINUTE_OF_HOUR, 5},
 156                 {MONTH_OF_YEAR, 5, SECOND_OF_MINUTE, 5},
 157                 {MONTH_OF_YEAR, 5, NANO_OF_SECOND, 5},
 158         };
 159     }
 160 
 161     @Test(dataProvider="resolveTwoNoChange")
 162     public void test_resolveTwoNoChange(TemporalField field1, long value1, TemporalField field2, long value2) {
 163         String str = value1 + " " + value2;
 164         DateTimeFormatter f = new DateTimeFormatterBuilder()
 165                 .appendValue(field1).appendLiteral(' ')
 166                 .appendValue(field2).toFormatter();
 167         TemporalAccessor accessor = f.parse(str);
 168 
 169         assertEquals(accessor.query(TemporalQuery.localDate()), null);
 170         assertEquals(accessor.query(TemporalQuery.localTime()), null);
 171         assertEquals(accessor.isSupported(field1), true);
 172         assertEquals(accessor.isSupported(field2), true);
 173         assertEquals(accessor.getLong(field1), value1);
 174         assertEquals(accessor.getLong(field2), value2);
 175     }
 176 
 177     //-----------------------------------------------------------------------
 178     @DataProvider(name="resolveThreeNoChange")
 179     Object[][] data_resolveThreeNoChange() {
 180         return new Object[][]{
 181                 {YEAR, 2012, MONTH_OF_YEAR, 5, DAY_OF_WEEK, 5},
 182                 {YEAR, 2012, ALIGNED_WEEK_OF_YEAR, 5, DAY_OF_MONTH, 5},
 183                 {YEAR, 2012, ALIGNED_WEEK_OF_MONTH, 5, DAY_OF_MONTH, 5},
 184                 {YEAR, 2012, MONTH_OF_YEAR, 5, DAY_OF_WEEK, 5},
 185                 {ERA, 1, MONTH_OF_YEAR, 5, DAY_OF_MONTH, 5},
 186                 {MONTH_OF_YEAR, 1, DAY_OF_MONTH, 5, IsoFields.QUARTER_OF_YEAR, 3},
 187                 {HOUR_OF_DAY, 1, SECOND_OF_MINUTE, 5, NANO_OF_SECOND, 5},
 188                 {MINUTE_OF_HOUR, 1, SECOND_OF_MINUTE, 5, NANO_OF_SECOND, 5},
 189         };
 190     }
 191 
 192     @Test(dataProvider="resolveThreeNoChange")
 193     public void test_resolveThreeNoChange(TemporalField field1, long value1, TemporalField field2, long value2, TemporalField field3, long value3) {
 194         String str = value1 + " " + value2 + " " + value3;
 195         DateTimeFormatter f = new DateTimeFormatterBuilder()
 196                 .appendValue(field1).appendLiteral(' ')
 197                 .appendValue(field2).appendLiteral(' ')
 198                 .appendValue(field3).toFormatter();
 199         TemporalAccessor accessor = f.parse(str);
 200 
 201         assertEquals(accessor.query(TemporalQuery.localDate()), null);
 202         assertEquals(accessor.query(TemporalQuery.localTime()), null);
 203         assertEquals(accessor.isSupported(field1), true);
 204         assertEquals(accessor.isSupported(field2), true);
 205         assertEquals(accessor.isSupported(field3), true);
 206         assertEquals(accessor.getLong(field1), value1);
 207         assertEquals(accessor.getLong(field2), value2);
 208         assertEquals(accessor.getLong(field3), value3);
 209     }
 210 
 211     //-----------------------------------------------------------------------
 212     //-----------------------------------------------------------------------
 213     //-----------------------------------------------------------------------
 214     @DataProvider(name="resolveOneToField")
 215     Object[][] data_resolveOneToField() {
 216         return new Object[][]{
 217                 {YEAR_OF_ERA, 2012, YEAR, 2012L, null, null},
 218                 {PROLEPTIC_MONTH, 2012 * 12L + (3 - 1), YEAR, 2012L, MONTH_OF_YEAR, 3L},
 219 
 220                 {CLOCK_HOUR_OF_AMPM, 8, HOUR_OF_AMPM, 8L, null, null},
 221                 {CLOCK_HOUR_OF_AMPM, 12, HOUR_OF_AMPM, 0L, null, null},
 222                 {MICRO_OF_SECOND, 12, NANO_OF_SECOND, 12_000L, null, null},
 223                 {MILLI_OF_SECOND, 12, NANO_OF_SECOND, 12_000_000L, null, null},
 224         };
 225     }
 226 
 227     @Test(dataProvider="resolveOneToField")
 228     public void test_resolveOneToField(TemporalField field1, long value1,
 229                                        TemporalField expectedField1, Long expectedValue1,
 230                                        TemporalField expectedField2, Long expectedValue2) {
 231         String str = Long.toString(value1);
 232         DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(field1).toFormatter();
 233 
 234         TemporalAccessor accessor = f.parse(str);
 235         assertEquals(accessor.query(TemporalQuery.localDate()), null);
 236         assertEquals(accessor.query(TemporalQuery.localTime()), null);
 237         if (expectedField1 != null) {
 238             assertEquals(accessor.isSupported(expectedField1), true);
 239             assertEquals(accessor.getLong(expectedField1), expectedValue1.longValue());
 240         }
 241         if (expectedField2 != null) {
 242             assertEquals(accessor.isSupported(expectedField2), true);
 243             assertEquals(accessor.getLong(expectedField2), expectedValue2.longValue());
 244         }
 245     }
 246 
 247     //-----------------------------------------------------------------------
 248     @DataProvider(name="resolveOneToDate")
 249     Object[][] data_resolveOneToDate() {
 250         return new Object[][]{
 251                 {EPOCH_DAY, 32, LocalDate.of(1970, 2, 2)},
 252         };
 253     }
 254 
 255     @Test(dataProvider="resolveOneToDate")
 256     public void test_resolveOneToDate(TemporalField field1, long value1, LocalDate expectedDate) {
 257         String str = Long.toString(value1);
 258         DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(field1).toFormatter();
 259 
 260         TemporalAccessor accessor = f.parse(str);
 261         assertEquals(accessor.query(TemporalQuery.localDate()), expectedDate);
 262         assertEquals(accessor.query(TemporalQuery.localTime()), null);
 263     }
 264 
 265     //-----------------------------------------------------------------------
 266     @DataProvider(name="resolveOneToTime")
 267     Object[][] data_resolveOneToTime() {
 268         return new Object[][]{
 269                 {HOUR_OF_DAY, 8, LocalTime.of(8, 0)},
 270                 {CLOCK_HOUR_OF_DAY, 8, LocalTime.of(8, 0)},
 271                 {CLOCK_HOUR_OF_DAY, 24, LocalTime.of(0, 0)},
 272                 {MINUTE_OF_DAY, 650, LocalTime.of(10, 50)},
 273                 {SECOND_OF_DAY, 3600 + 650, LocalTime.of(1, 10, 50)},
 274                 {MILLI_OF_DAY, (3600 + 650) * 1_000L + 2, LocalTime.of(1, 10, 50, 2_000_000)},
 275                 {MICRO_OF_DAY, (3600 + 650) * 1_000_000L + 2, LocalTime.of(1, 10, 50, 2_000)},
 276                 {NANO_OF_DAY, (3600 + 650) * 1_000_000_000L + 2, LocalTime.of(1, 10, 50, 2)},
 277         };
 278     }
 279 
 280     @Test(dataProvider="resolveOneToTime")
 281     public void test_resolveOneToTime(TemporalField field1, long value1, LocalTime expectedTime) {
 282         String str = Long.toString(value1);
 283         DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(field1).toFormatter();
 284 
 285         TemporalAccessor accessor = f.parse(str);
 286         assertEquals(accessor.query(TemporalQuery.localDate()), null);
 287         assertEquals(accessor.query(TemporalQuery.localTime()), expectedTime);
 288     }
 289 
 290     //-----------------------------------------------------------------------
 291     //-----------------------------------------------------------------------
 292     //-----------------------------------------------------------------------
 293     @DataProvider(name="resolveTwoToField")
 294     Object[][] data_resolveTwoToField() {
 295         return new Object[][]{
 296                 // cross-check
 297                 {PROLEPTIC_MONTH, 2012 * 12L + (3 - 1), YEAR, 2012, YEAR, 2012L, MONTH_OF_YEAR, 3L},
 298                 {PROLEPTIC_MONTH, 2012 * 12L + (3 - 1), YEAR_OF_ERA, 2012, YEAR, 2012L, MONTH_OF_YEAR, 3L},
 299                 {PROLEPTIC_MONTH, 2012 * 12L + (3 - 1), ERA, 1, YEAR, 2012L, MONTH_OF_YEAR, 3L},
 300                 {PROLEPTIC_MONTH, (3 - 1), YEAR, 0, YEAR, 0L, MONTH_OF_YEAR, 3L},
 301                 {PROLEPTIC_MONTH, (3 - 1), YEAR_OF_ERA, 1, YEAR, 0L, MONTH_OF_YEAR, 3L},
 302                 {PROLEPTIC_MONTH, (3 - 1), ERA, 0, YEAR, 0L, MONTH_OF_YEAR, 3L},
 303         };
 304     }
 305 
 306     @Test(dataProvider="resolveTwoToField")
 307     public void test_resolveTwoToField(TemporalField field1, long value1,
 308                                        TemporalField field2, long value2,
 309                                        TemporalField expectedField1, Long expectedValue1,
 310                                        TemporalField expectedField2, Long expectedValue2) {
 311         String str = value1 + " " + value2;
 312         DateTimeFormatter f = new DateTimeFormatterBuilder()
 313                 .appendValue(field1).appendLiteral(' ')
 314                 .appendValue(field2).toFormatter();
 315 
 316         TemporalAccessor accessor = f.parse(str);
 317         assertEquals(accessor.query(TemporalQuery.localDate()), null);
 318         assertEquals(accessor.query(TemporalQuery.localTime()), null);
 319         if (expectedField1 != null) {
 320             assertEquals(accessor.isSupported(expectedField1), true);
 321             assertEquals(accessor.getLong(expectedField1), expectedValue1.longValue());
 322         }
 323         if (expectedField2 != null) {
 324             assertEquals(accessor.isSupported(expectedField2), true);
 325             assertEquals(accessor.getLong(expectedField2), expectedValue2.longValue());
 326         }
 327     }
 328 
 329     //-----------------------------------------------------------------------
 330     @DataProvider(name="resolveTwoToDate")
 331     Object[][] data_resolveTwoToDate() {
 332         return new Object[][]{
 333                 // merge
 334                 {YEAR, 2012, DAY_OF_YEAR, 32, LocalDate.of(2012, 2, 1)},
 335                 {YEAR_OF_ERA, 2012, DAY_OF_YEAR, 32, LocalDate.of(2012, 2, 1)},
 336 
 337                 // merge
 338                 {PROLEPTIC_MONTH, 2012 * 12 + (2 - 1), DAY_OF_MONTH, 25, LocalDate.of(2012, 2, 25)},
 339                 {PROLEPTIC_MONTH, 2012 * 12 + (2 - 1), DAY_OF_YEAR, 56, LocalDate.of(2012, 2, 25)},
 340 
 341                 // cross-check
 342                 {EPOCH_DAY, 32, ERA, 1, LocalDate.of(1970, 2, 2)},
 343                 {EPOCH_DAY, -146097 * 5L, ERA, 0, LocalDate.of(1970 - (400 * 5), 1, 1)},
 344                 {EPOCH_DAY, 32, YEAR, 1970, LocalDate.of(1970, 2, 2)},
 345                 {EPOCH_DAY, -146097 * 5L, YEAR, 1970 - (400 * 5), LocalDate.of(1970 - (400 * 5), 1, 1)},
 346                 {EPOCH_DAY, 32, YEAR_OF_ERA, 1970, LocalDate.of(1970, 2, 2)},
 347                 {EPOCH_DAY, -146097 * 5L, YEAR_OF_ERA, 1 - (1970 - (400 * 5)), LocalDate.of(1970 - (400 * 5), 1, 1)},
 348                 {EPOCH_DAY, 32, MONTH_OF_YEAR, 2, LocalDate.of(1970, 2, 2)},
 349                 {EPOCH_DAY, 32, DAY_OF_YEAR, 33, LocalDate.of(1970, 2, 2)},
 350                 {EPOCH_DAY, 32, DAY_OF_MONTH, 2, LocalDate.of(1970, 2, 2)},
 351                 {EPOCH_DAY, 32, DAY_OF_WEEK, 1, LocalDate.of(1970, 2, 2)},
 352         };
 353     }
 354 
 355     @Test(dataProvider="resolveTwoToDate")
 356     public void test_resolveTwoToDate(TemporalField field1, long value1,
 357                                       TemporalField field2, long value2,
 358                                       LocalDate expectedDate) {
 359         String str = value1 + " " + value2;
 360         DateTimeFormatter f = new DateTimeFormatterBuilder()
 361                 .appendValue(field1).appendLiteral(' ')
 362                 .appendValue(field2).toFormatter();
 363 
 364         TemporalAccessor accessor = f.parse(str);
 365         assertEquals(accessor.query(TemporalQuery.localDate()), expectedDate);
 366         assertEquals(accessor.query(TemporalQuery.localTime()), null);
 367     }
 368 
 369     //-----------------------------------------------------------------------
 370     @DataProvider(name="resolveTwoToTime")
 371     Object[][] data_resolveTwoToTime() {
 372         return new Object[][]{
 373                 // merge
 374                 {HOUR_OF_DAY, 8, MINUTE_OF_HOUR, 6, LocalTime.of(8, 6)},
 375 
 376                 // merge
 377                 {AMPM_OF_DAY, 0, HOUR_OF_AMPM, 5, LocalTime.of(5, 0)},
 378                 {AMPM_OF_DAY, 1, HOUR_OF_AMPM, 5, LocalTime.of(17, 0)},
 379                 {AMPM_OF_DAY, 0, CLOCK_HOUR_OF_AMPM, 5, LocalTime.of(5, 0)},
 380                 {AMPM_OF_DAY, 1, CLOCK_HOUR_OF_AMPM, 5, LocalTime.of(17, 0)},
 381                 {AMPM_OF_DAY, 0, HOUR_OF_DAY, 5, LocalTime.of(5, 0)},
 382                 {AMPM_OF_DAY, 1, HOUR_OF_DAY, 17, LocalTime.of(17, 0)},
 383                 {AMPM_OF_DAY, 0, CLOCK_HOUR_OF_DAY, 5, LocalTime.of(5, 0)},
 384                 {AMPM_OF_DAY, 1, CLOCK_HOUR_OF_DAY, 17, LocalTime.of(17, 0)},
 385 
 386                 // merge
 387                 {CLOCK_HOUR_OF_DAY, 8, MINUTE_OF_HOUR, 6, LocalTime.of(8, 6)},
 388                 {CLOCK_HOUR_OF_DAY, 24, MINUTE_OF_HOUR, 6, LocalTime.of(0, 6)},
 389                 // cross-check
 390                 {CLOCK_HOUR_OF_DAY, 8, HOUR_OF_DAY, 8, LocalTime.of(8, 0)},
 391                 {CLOCK_HOUR_OF_DAY, 8, CLOCK_HOUR_OF_AMPM, 8, LocalTime.of(8, 0)},
 392                 {CLOCK_HOUR_OF_DAY, 20, CLOCK_HOUR_OF_AMPM, 8, LocalTime.of(20, 0)},
 393                 {CLOCK_HOUR_OF_DAY, 8, AMPM_OF_DAY, 0, LocalTime.of(8, 0)},
 394                 {CLOCK_HOUR_OF_DAY, 20, AMPM_OF_DAY, 1, LocalTime.of(20, 0)},
 395 
 396                 // merge
 397                 {MINUTE_OF_DAY, 650, SECOND_OF_MINUTE, 8, LocalTime.of(10, 50, 8)},
 398                 // cross-check
 399                 {MINUTE_OF_DAY, 650, HOUR_OF_DAY, 10, LocalTime.of(10, 50)},
 400                 {MINUTE_OF_DAY, 650, CLOCK_HOUR_OF_DAY, 10, LocalTime.of(10, 50)},
 401                 {MINUTE_OF_DAY, 650, CLOCK_HOUR_OF_AMPM, 10, LocalTime.of(10, 50)},
 402                 {MINUTE_OF_DAY, 650, AMPM_OF_DAY, 0, LocalTime.of(10, 50)},
 403                 {MINUTE_OF_DAY, 650, MINUTE_OF_HOUR, 50, LocalTime.of(10, 50)},
 404 
 405                 // merge
 406                 {SECOND_OF_DAY, 3600 + 650, MILLI_OF_SECOND, 2, LocalTime.of(1, 10, 50, 2_000_000)},
 407                 {SECOND_OF_DAY, 3600 + 650, MICRO_OF_SECOND, 2, LocalTime.of(1, 10, 50, 2_000)},
 408                 {SECOND_OF_DAY, 3600 + 650, NANO_OF_SECOND, 2, LocalTime.of(1, 10, 50, 2)},
 409                 // cross-check
 410                 {SECOND_OF_DAY, 3600 + 650, HOUR_OF_DAY, 1, LocalTime.of(1, 10, 50)},
 411                 {SECOND_OF_DAY, 3600 + 650, MINUTE_OF_HOUR, 10, LocalTime.of(1, 10, 50)},
 412                 {SECOND_OF_DAY, 3600 + 650, SECOND_OF_MINUTE, 50, LocalTime.of(1, 10, 50)},
 413 
 414                 // merge
 415                 {MILLI_OF_DAY, (3600 + 650) * 1000L + 2, MICRO_OF_SECOND, 2_004, LocalTime.of(1, 10, 50, 2_004_000)},
 416                 {MILLI_OF_DAY, (3600 + 650) * 1000L + 2, NANO_OF_SECOND, 2_000_004, LocalTime.of(1, 10, 50, 2_000_004)},
 417                 // cross-check
 418                 {MILLI_OF_DAY, (3600 + 650) * 1000L + 2, HOUR_OF_DAY, 1, LocalTime.of(1, 10, 50, 2_000_000)},
 419                 {MILLI_OF_DAY, (3600 + 650) * 1000L + 2, MINUTE_OF_HOUR, 10, LocalTime.of(1, 10, 50, 2_000_000)},
 420                 {MILLI_OF_DAY, (3600 + 650) * 1000L + 2, SECOND_OF_MINUTE, 50, LocalTime.of(1, 10, 50, 2_000_000)},
 421                 {MILLI_OF_DAY, (3600 + 650) * 1000L + 2, MILLI_OF_SECOND, 2, LocalTime.of(1, 10, 50, 2_000_000)},
 422 
 423                 // merge
 424                 {MICRO_OF_DAY, (3600 + 650) * 1000_000L + 2, NANO_OF_SECOND, 2_004, LocalTime.of(1, 10, 50, 2_004)},
 425                 // cross-check
 426                 {MICRO_OF_DAY, (3600 + 650) * 1000_000L + 2, HOUR_OF_DAY, 1, LocalTime.of(1, 10, 50, 2_000)},
 427                 {MICRO_OF_DAY, (3600 + 650) * 1000_000L + 2, MINUTE_OF_HOUR, 10, LocalTime.of(1, 10, 50, 2_000)},
 428                 {MICRO_OF_DAY, (3600 + 650) * 1000_000L + 2, SECOND_OF_MINUTE, 50, LocalTime.of(1, 10, 50, 2_000)},
 429                 {MICRO_OF_DAY, (3600 + 650) * 1000_000L + 2, MILLI_OF_SECOND, 0, LocalTime.of(1, 10, 50, 2_000)},
 430                 {MICRO_OF_DAY, (3600 + 650) * 1000_000L + 2, MICRO_OF_SECOND, 2, LocalTime.of(1, 10, 50, 2_000)},
 431 
 432                 // cross-check
 433                 {NANO_OF_DAY, (3600 + 650) * 1000_000_000L + 2, HOUR_OF_DAY, 1, LocalTime.of(1, 10, 50, 2)},
 434                 {NANO_OF_DAY, (3600 + 650) * 1000_000_000L + 2, MINUTE_OF_HOUR, 10, LocalTime.of(1, 10, 50, 2)},
 435                 {NANO_OF_DAY, (3600 + 650) * 1000_000_000L + 2, SECOND_OF_MINUTE, 50, LocalTime.of(1, 10, 50, 2)},
 436                 {NANO_OF_DAY, (3600 + 650) * 1000_000_000L + 2, MILLI_OF_SECOND, 0, LocalTime.of(1, 10, 50, 2)},
 437                 {NANO_OF_DAY, (3600 + 650) * 1000_000_000L + 2, MICRO_OF_SECOND, 0, LocalTime.of(1, 10, 50, 2)},
 438                 {NANO_OF_DAY, (3600 + 650) * 1000_000_000L + 2, NANO_OF_SECOND, 2, LocalTime.of(1, 10, 50, 2)},
 439         };
 440     }
 441 
 442     @Test(dataProvider="resolveTwoToTime")
 443     public void test_resolveTwoToTime(TemporalField field1, long value1,
 444                                 TemporalField field2, long value2,
 445                                 LocalTime expectedTime) {
 446         String str = value1 + " " + value2;
 447         DateTimeFormatter f = new DateTimeFormatterBuilder()
 448                 .appendValue(field1).appendLiteral(' ')
 449                 .appendValue(field2).toFormatter();
 450 
 451         TemporalAccessor accessor = f.parse(str);
 452         assertEquals(accessor.query(TemporalQuery.localDate()), null);
 453         assertEquals(accessor.query(TemporalQuery.localTime()), expectedTime);
 454     }
 455 
 456     //-----------------------------------------------------------------------
 457     @DataProvider(name="resolveThreeToDate")
 458     Object[][] data_resolveThreeToDate() {
 459         return new Object[][]{
 460                 // merge
 461                 {YEAR, 2012, MONTH_OF_YEAR, 2, DAY_OF_MONTH, 1, LocalDate.of(2012, 2, 1)},
 462                 {YEAR, 2012, ALIGNED_WEEK_OF_YEAR, 5, ALIGNED_DAY_OF_WEEK_IN_YEAR, 4, LocalDate.of(2012, 2, 1)},
 463                 {YEAR, 2012, ALIGNED_WEEK_OF_YEAR, 5, DAY_OF_WEEK, 3, LocalDate.of(2012, 2, 1)},
 464 
 465                 // cross-check
 466                 {YEAR, 2012, DAY_OF_YEAR, 32, DAY_OF_MONTH, 1, LocalDate.of(2012, 2, 1)},
 467                 {YEAR_OF_ERA, 2012, DAY_OF_YEAR, 32, DAY_OF_MONTH, 1, LocalDate.of(2012, 2, 1)},
 468                 {YEAR, 2012, DAY_OF_YEAR, 32, DAY_OF_WEEK, 3, LocalDate.of(2012, 2, 1)},
 469                 {PROLEPTIC_MONTH, 2012 * 12 + (2 - 1), DAY_OF_MONTH, 25, DAY_OF_WEEK, 6, LocalDate.of(2012, 2, 25)},
 470         };
 471     }
 472 
 473     @Test(dataProvider="resolveThreeToDate")
 474     public void test_resolveThreeToDate(TemporalField field1, long value1,
 475                                       TemporalField field2, long value2,
 476                                       TemporalField field3, long value3,
 477                                       LocalDate expectedDate) {
 478         String str = value1 + " " + value2 + " " + value3;
 479         DateTimeFormatter f = new DateTimeFormatterBuilder()
 480                 .appendValue(field1).appendLiteral(' ')
 481                 .appendValue(field2).appendLiteral(' ')
 482                 .appendValue(field3).toFormatter();
 483 
 484         TemporalAccessor accessor = f.parse(str);
 485         assertEquals(accessor.query(TemporalQuery.localDate()), expectedDate);
 486         assertEquals(accessor.query(TemporalQuery.localTime()), null);
 487     }
 488 
 489     //-----------------------------------------------------------------------
 490     @DataProvider(name="resolveFourToDate")
 491     Object[][] data_resolveFourToDate() {
 492         return new Object[][]{
 493                 // merge
 494                 {YEAR, 2012, MONTH_OF_YEAR, 2, ALIGNED_WEEK_OF_MONTH, 1, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1, LocalDate.of(2012, 2, 1)},
 495                 {YEAR, 2012, MONTH_OF_YEAR, 2, ALIGNED_WEEK_OF_MONTH, 1, DAY_OF_WEEK, 3, LocalDate.of(2012, 2, 1)},
 496 
 497                 // cross-check
 498                 {YEAR, 2012, MONTH_OF_YEAR, 2, DAY_OF_MONTH, 1, DAY_OF_WEEK, 3, LocalDate.of(2012, 2, 1)},
 499                 {YEAR, 2012, ALIGNED_WEEK_OF_YEAR, 5, ALIGNED_DAY_OF_WEEK_IN_YEAR, 4, DAY_OF_WEEK, 3, LocalDate.of(2012, 2, 1)},
 500                 {YEAR, 2012, ALIGNED_WEEK_OF_YEAR, 5, DAY_OF_WEEK, 3, DAY_OF_MONTH, 1, LocalDate.of(2012, 2, 1)},
 501         };
 502     }
 503 
 504     @Test(dataProvider="resolveFourToDate")
 505     public void test_resolveFourToDate(TemporalField field1, long value1,
 506                                         TemporalField field2, long value2,
 507                                         TemporalField field3, long value3,
 508                                         TemporalField field4, long value4,
 509                                         LocalDate expectedDate) {
 510         String str = value1 + " " + value2 + " " + value3 + " " + value4;
 511         DateTimeFormatter f = new DateTimeFormatterBuilder()
 512                 .appendValue(field1).appendLiteral(' ')
 513                 .appendValue(field2).appendLiteral(' ')
 514                 .appendValue(field3).appendLiteral(' ')
 515                 .appendValue(field4).toFormatter();
 516 
 517         TemporalAccessor accessor = f.parse(str);
 518         assertEquals(accessor.query(TemporalQuery.localDate()), expectedDate);
 519         assertEquals(accessor.query(TemporalQuery.localTime()), null);
 520     }
 521 
 522 }