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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos
  28  *
  29  * All rights reserved.
  30  *
  31  * Redistribution and use in source and binary forms, with or without
  32  * modification, are permitted provided that the following conditions are met:
  33  *
  34  *  * Redistributions of source code must retain the above copyright notice,
  35  *    this list of conditions and the following disclaimer.
  36  *
  37  *  * Redistributions in binary form must reproduce the above copyright notice,
  38  *    this list of conditions and the following disclaimer in the documentation
  39  *    and/or other materials provided with the distribution.
  40  *
  41  *  * Neither the name of JSR-310 nor the names of its contributors
  42  *    may be used to endorse or promote products derived from this software
  43  *    without specific prior written permission.
  44  *
  45  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  46  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  47  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  48  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  49  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  50  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  51  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  52  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  53  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  54  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  55  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  56  */
  57 package tck.java.time.temporal;
  58 
  59 import static java.time.DayOfWeek.FRIDAY;
  60 import static java.time.DayOfWeek.MONDAY;
  61 import static java.time.DayOfWeek.SATURDAY;
  62 import static java.time.DayOfWeek.SUNDAY;
  63 import static java.time.DayOfWeek.THURSDAY;
  64 import static java.time.DayOfWeek.TUESDAY;
  65 import static java.time.DayOfWeek.WEDNESDAY;
  66 import static java.time.temporal.ChronoField.DAY_OF_WEEK;
  67 import static java.time.temporal.ChronoField.YEAR;
  68 import static org.testng.Assert.assertEquals;
  69 import static org.testng.Assert.fail;
  70 
  71 import java.time.DayOfWeek;
  72 import java.time.LocalDate;
  73 import java.time.format.DateTimeFormatter;
  74 import java.time.format.DateTimeFormatterBuilder;
  75 import java.time.format.DateTimeParseException;
  76 import java.time.format.ResolverStyle;
  77 import java.time.temporal.IsoFields;
  78 import java.time.temporal.ValueRange;
  79 
  80 import org.testng.annotations.DataProvider;
  81 import org.testng.annotations.Test;
  82 
  83 /**
  84  * Test.
  85  */
  86 @Test
  87 public class TCKIsoFields {
  88 
  89     @DataProvider(name="quarter")
  90     Object[][] data_quarter() {
  91         return new Object[][] {
  92                 {LocalDate.of(1969, 12, 29), 90, 4},
  93                 {LocalDate.of(1969, 12, 30), 91, 4},
  94                 {LocalDate.of(1969, 12, 31), 92, 4},
  95 
  96                 {LocalDate.of(1970, 1, 1), 1, 1},
  97                 {LocalDate.of(1970, 1, 2), 2, 1},
  98                 {LocalDate.of(1970, 2, 28), 59, 1},
  99                 {LocalDate.of(1970, 3, 1), 60, 1},
 100                 {LocalDate.of(1970, 3, 31), 90, 1},
 101 
 102                 {LocalDate.of(1970, 4, 1), 1, 2},
 103                 {LocalDate.of(1970, 6, 30), 91, 2},
 104 
 105                 {LocalDate.of(1970, 7, 1), 1, 3},
 106                 {LocalDate.of(1970, 9, 30), 92, 3},
 107 
 108                 {LocalDate.of(1970, 10, 1), 1, 4},
 109                 {LocalDate.of(1970, 12, 31), 92, 4},
 110 
 111                 {LocalDate.of(1972, 2, 28), 59, 1},
 112                 {LocalDate.of(1972, 2, 29), 60, 1},
 113                 {LocalDate.of(1972, 3, 1), 61, 1},
 114                 {LocalDate.of(1972, 3, 31), 91, 1},
 115         };
 116     }
 117 
 118     //-----------------------------------------------------------------------
 119     // DAY_OF_QUARTER
 120     //-----------------------------------------------------------------------
 121     @Test(dataProvider = "quarter")
 122     public void test_DOQ(LocalDate date, int doq, int qoy) {
 123         assertEquals(IsoFields.DAY_OF_QUARTER.getFrom(date), doq);
 124         assertEquals(date.get(IsoFields.DAY_OF_QUARTER), doq);
 125     }
 126 
 127     public void test_DOQ_basics() {
 128         assertEquals(IsoFields.DAY_OF_QUARTER.isDateBased(), true);
 129         assertEquals(IsoFields.DAY_OF_QUARTER.isTimeBased(), false);
 130     }
 131 
 132     //-----------------------------------------------------------------------
 133     // QUARTER_OF_YEAR
 134     //-----------------------------------------------------------------------
 135     @Test(dataProvider = "quarter")
 136     public void test_QOY(LocalDate date, int doq, int qoy) {
 137         assertEquals(IsoFields.QUARTER_OF_YEAR.getFrom(date), qoy);
 138         assertEquals(date.get(IsoFields.QUARTER_OF_YEAR), qoy);
 139     }
 140 
 141     public void test_QOY_basics() {
 142         assertEquals(IsoFields.QUARTER_OF_YEAR.isDateBased(), true);
 143         assertEquals(IsoFields.QUARTER_OF_YEAR.isTimeBased(), false);
 144     }
 145 
 146     //-----------------------------------------------------------------------
 147     // parse quarters
 148     //-----------------------------------------------------------------------
 149     @Test(dataProvider = "quarter")
 150     public void test_parse_quarters(LocalDate date, int doq, int qoy) {
 151         DateTimeFormatter f = new DateTimeFormatterBuilder()
 152                 .appendValue(YEAR).appendLiteral('-')
 153                 .appendValue(IsoFields.QUARTER_OF_YEAR).appendLiteral('-')
 154                 .appendValue(IsoFields.DAY_OF_QUARTER)
 155                 .toFormatter().withResolverStyle(ResolverStyle.STRICT);
 156         LocalDate parsed = LocalDate.parse(date.getYear() + "-" + qoy + "-" + doq, f);
 157         assertEquals(parsed, date);
 158     }
 159 
 160     @Test(dataProvider = "quarter")
 161     public void test_parse_quarters_SMART(LocalDate date, int doq, int qoy) {
 162         DateTimeFormatter f = new DateTimeFormatterBuilder()
 163                 .appendValue(YEAR).appendLiteral('-')
 164                 .appendValue(IsoFields.QUARTER_OF_YEAR).appendLiteral('-')
 165                 .appendValue(IsoFields.DAY_OF_QUARTER)
 166                 .toFormatter().withResolverStyle(ResolverStyle.SMART);
 167         LocalDate parsed = LocalDate.parse(date.getYear() + "-" + qoy + "-" + doq, f);
 168         assertEquals(parsed, date);
 169     }
 170 
 171     @Test(dataProvider = "quarter")
 172     public void test_parse_quarters_LENIENT(LocalDate date, int doq, int qoy) {
 173         DateTimeFormatter f = new DateTimeFormatterBuilder()
 174                 .appendValue(YEAR).appendLiteral('-')
 175                 .appendValue(IsoFields.QUARTER_OF_YEAR).appendLiteral('-')
 176                 .appendValue(IsoFields.DAY_OF_QUARTER)
 177                 .toFormatter().withResolverStyle(ResolverStyle.LENIENT);
 178         LocalDate parsed = LocalDate.parse(date.getYear() + "-" + qoy + "-" + doq, f);
 179         assertEquals(parsed, date);
 180     }
 181 
 182     //-----------------------------------------------------------------------
 183     @DataProvider(name="parseLenientQuarter")
 184     Object[][] data_parseLenientQuarter() {
 185         return new Object[][] {
 186                 {"2012:0:1", LocalDate.of(2011, 10, 1), false},
 187                 {"2012:5:1", LocalDate.of(2013, 1, 1), false},
 188 
 189                 {"2012:1:-1", LocalDate.of(2011, 12, 30), false},
 190                 {"2012:1:0", LocalDate.of(2011, 12, 31), false},
 191                 {"2012:0:0", LocalDate.of(2011, 9, 30), false},
 192 
 193                 {"2012:1:92", LocalDate.of(2012, 4, 1), true},
 194                 {"2012:2:92", LocalDate.of(2012, 7, 1), true},
 195                 {"2012:2:93", LocalDate.of(2012, 7, 2), false},
 196                 {"2012:3:93", LocalDate.of(2012, 10, 1), false},
 197                 {"2012:4:93", LocalDate.of(2013, 1, 1), false},
 198                 {"2012:4:182", LocalDate.of(2013, 3, 31), false},
 199                 {"2012:4:183", LocalDate.of(2013, 4, 1), false},
 200 
 201                 {"2011:1:91", LocalDate.of(2011, 4, 1), true},
 202                 {"2011:1:92", LocalDate.of(2011, 4, 2), true},
 203         };
 204     }
 205 
 206     @Test(dataProvider = "parseLenientQuarter", expectedExceptions = DateTimeParseException.class)
 207     public void test_parse_parseLenientQuarter_STRICT(String str, LocalDate expected, boolean smart) {
 208         DateTimeFormatter f = new DateTimeFormatterBuilder()
 209                 .appendValue(YEAR).appendLiteral(':')
 210                 .appendValue(IsoFields.QUARTER_OF_YEAR).appendLiteral(':')
 211                 .appendValue(IsoFields.DAY_OF_QUARTER)
 212                 .toFormatter().withResolverStyle(ResolverStyle.STRICT);
 213         LocalDate.parse(str, f);
 214     }
 215 
 216     @Test(dataProvider = "parseLenientQuarter")
 217     public void test_parse_parseLenientQuarter_SMART(String str, LocalDate expected, boolean smart) {
 218         DateTimeFormatter f = new DateTimeFormatterBuilder()
 219                 .appendValue(YEAR).appendLiteral(':')
 220                 .appendValue(IsoFields.QUARTER_OF_YEAR).appendLiteral(':')
 221                 .appendValue(IsoFields.DAY_OF_QUARTER)
 222                 .toFormatter().withResolverStyle(ResolverStyle.SMART);
 223         if (smart) {
 224             LocalDate parsed = LocalDate.parse(str, f);
 225             assertEquals(parsed, expected);
 226         } else {
 227             try {
 228                 LocalDate.parse(str, f);
 229                 fail("Should have failed");
 230             } catch (DateTimeParseException ex) {
 231                 // expected
 232             }
 233         }
 234     }
 235 
 236     @Test(dataProvider = "parseLenientQuarter")
 237     public void test_parse_parseLenientQuarter_LENIENT(String str, LocalDate expected, boolean smart) {
 238         DateTimeFormatter f = new DateTimeFormatterBuilder()
 239                 .appendValue(YEAR).appendLiteral(':')
 240                 .appendValue(IsoFields.QUARTER_OF_YEAR).appendLiteral(':')
 241                 .appendValue(IsoFields.DAY_OF_QUARTER)
 242                 .toFormatter().withResolverStyle(ResolverStyle.LENIENT);
 243         LocalDate parsed = LocalDate.parse(str, f);
 244         assertEquals(parsed, expected);
 245     }
 246 
 247     //-----------------------------------------------------------------------
 248     // quarters between
 249     //-----------------------------------------------------------------------
 250     @DataProvider(name="quartersBetween")
 251     Object[][] data_quartersBetween() {
 252         return new Object[][] {
 253                 {LocalDate.of(2000, 1, 1), LocalDate.of(2000, 1, 1), 0},
 254                 {LocalDate.of(2000, 1, 1), LocalDate.of(2000, 1, 2), 0},
 255                 {LocalDate.of(2000, 1, 1), LocalDate.of(2000, 2, 1), 0},
 256                 {LocalDate.of(2000, 1, 1), LocalDate.of(2000, 3, 1), 0},
 257                 {LocalDate.of(2000, 1, 1), LocalDate.of(2000, 3, 31), 0},
 258                 {LocalDate.of(2000, 1, 1), LocalDate.of(2000, 4, 1), 1},
 259                 {LocalDate.of(2000, 1, 1), LocalDate.of(2000, 4, 2), 1},
 260                 {LocalDate.of(2000, 1, 1), LocalDate.of(2000, 6, 30), 1},
 261                 {LocalDate.of(2000, 1, 1), LocalDate.of(2000, 7, 1), 2},
 262                 {LocalDate.of(2000, 1, 1), LocalDate.of(2000, 10, 1), 3},
 263                 {LocalDate.of(2000, 1, 1), LocalDate.of(2000, 12, 31), 3},
 264                 {LocalDate.of(2000, 1, 1), LocalDate.of(2001, 1, 1), 4},
 265                 {LocalDate.of(2000, 1, 1), LocalDate.of(2002, 1, 1), 8},
 266 
 267                 {LocalDate.of(2000, 1, 1), LocalDate.of(1999, 12, 31), 0},
 268                 {LocalDate.of(2000, 1, 1), LocalDate.of(1999, 10, 2), 0},
 269                 {LocalDate.of(2000, 1, 1), LocalDate.of(1999, 10, 1), -1},
 270                 {LocalDate.of(2000, 1, 1), LocalDate.of(1999, 7, 2), -1},
 271                 {LocalDate.of(2000, 1, 1), LocalDate.of(1999, 7, 1), -2},
 272                 {LocalDate.of(2000, 1, 1), LocalDate.of(1999, 4, 2), -2},
 273                 {LocalDate.of(2000, 1, 1), LocalDate.of(1999, 4, 1), -3},
 274                 {LocalDate.of(2000, 1, 1), LocalDate.of(1999, 1, 2), -3},
 275                 {LocalDate.of(2000, 1, 1), LocalDate.of(1999, 1, 1), -4},
 276                 {LocalDate.of(2000, 1, 1), LocalDate.of(1998, 12, 31), -4},
 277                 {LocalDate.of(2000, 1, 1), LocalDate.of(1998, 10, 2), -4},
 278                 {LocalDate.of(2000, 1, 1), LocalDate.of(1998, 10, 1), -5},
 279         };
 280     }
 281 
 282     @Test(dataProvider="quartersBetween")
 283     public void test_quarters_between(LocalDate start, LocalDate end, long expected) {
 284         assertEquals(IsoFields.QUARTER_YEARS.between(start, end), expected);
 285     }
 286 
 287     //-----------------------------------------------------------------------
 288     //-----------------------------------------------------------------------
 289     //-----------------------------------------------------------------------
 290     @DataProvider(name="week")
 291     Object[][] data_week() {
 292         return new Object[][] {
 293                 {LocalDate.of(1969, 12, 29), MONDAY, 1, 1970},
 294                 {LocalDate.of(2012, 12, 23), SUNDAY, 51, 2012},
 295                 {LocalDate.of(2012, 12, 24), MONDAY, 52, 2012},
 296                 {LocalDate.of(2012, 12, 27), THURSDAY, 52, 2012},
 297                 {LocalDate.of(2012, 12, 28), FRIDAY, 52, 2012},
 298                 {LocalDate.of(2012, 12, 29), SATURDAY, 52, 2012},
 299                 {LocalDate.of(2012, 12, 30), SUNDAY, 52, 2012},
 300                 {LocalDate.of(2012, 12, 31), MONDAY, 1, 2013},
 301                 {LocalDate.of(2013, 1, 1), TUESDAY, 1, 2013},
 302                 {LocalDate.of(2013, 1, 2), WEDNESDAY, 1, 2013},
 303                 {LocalDate.of(2013, 1, 6), SUNDAY, 1, 2013},
 304                 {LocalDate.of(2013, 1, 7), MONDAY, 2, 2013},
 305         };
 306     }
 307 
 308     //-----------------------------------------------------------------------
 309     // WEEK_OF_WEEK_BASED_YEAR
 310     //-----------------------------------------------------------------------
 311     @Test(dataProvider="week")
 312     public void test_WOWBY(LocalDate date, DayOfWeek dow, int week, int wby) {
 313         assertEquals(date.getDayOfWeek(), dow);
 314         assertEquals(IsoFields.WEEK_OF_WEEK_BASED_YEAR.getFrom(date), week);
 315         assertEquals(date.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR), week);
 316     }
 317 
 318     public void test_WOWBY_basics() {
 319         assertEquals(IsoFields.WEEK_OF_WEEK_BASED_YEAR.isDateBased(), true);
 320         assertEquals(IsoFields.WEEK_OF_WEEK_BASED_YEAR.isTimeBased(), false);
 321     }
 322 
 323     //-----------------------------------------------------------------------
 324     // WEEK_BASED_YEAR
 325     //-----------------------------------------------------------------------
 326     @Test(dataProvider="week")
 327     public void test_WBY(LocalDate date, DayOfWeek dow, int week, int wby) {
 328         assertEquals(date.getDayOfWeek(), dow);
 329         assertEquals(IsoFields.WEEK_BASED_YEAR.getFrom(date), wby);
 330         assertEquals(date.get(IsoFields.WEEK_BASED_YEAR), wby);
 331     }
 332 
 333     public void test_WBY_basics() {
 334         assertEquals(IsoFields.WEEK_BASED_YEAR.isDateBased(), true);
 335         assertEquals(IsoFields.WEEK_BASED_YEAR.isTimeBased(), false);
 336     }
 337 
 338     //-----------------------------------------------------------------------
 339     // parse weeks
 340     //-----------------------------------------------------------------------
 341     @Test(dataProvider="week")
 342     public void test_parse_weeks_STRICT(LocalDate date, DayOfWeek dow, int week, int wby) {
 343         DateTimeFormatter f = new DateTimeFormatterBuilder()
 344                 .appendValue(IsoFields.WEEK_BASED_YEAR).appendLiteral('-')
 345                 .appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR).appendLiteral('-')
 346                 .appendValue(DAY_OF_WEEK)
 347                 .toFormatter().withResolverStyle(ResolverStyle.STRICT);
 348         LocalDate parsed = LocalDate.parse(wby + "-" + week + "-" + dow.getValue(), f);
 349         assertEquals(parsed, date);
 350     }
 351 
 352     @Test(dataProvider="week")
 353     public void test_parse_weeks_SMART(LocalDate date, DayOfWeek dow, int week, int wby) {
 354         DateTimeFormatter f = new DateTimeFormatterBuilder()
 355                 .appendValue(IsoFields.WEEK_BASED_YEAR).appendLiteral('-')
 356                 .appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR).appendLiteral('-')
 357                 .appendValue(DAY_OF_WEEK)
 358                 .toFormatter().withResolverStyle(ResolverStyle.SMART);
 359         LocalDate parsed = LocalDate.parse(wby + "-" + week + "-" + dow.getValue(), f);
 360         assertEquals(parsed, date);
 361     }
 362 
 363     @Test(dataProvider="week")
 364     public void test_parse_weeks_LENIENT(LocalDate date, DayOfWeek dow, int week, int wby) {
 365         DateTimeFormatter f = new DateTimeFormatterBuilder()
 366                 .appendValue(IsoFields.WEEK_BASED_YEAR).appendLiteral('-')
 367                 .appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR).appendLiteral('-')
 368                 .appendValue(DAY_OF_WEEK)
 369                 .toFormatter().withResolverStyle(ResolverStyle.LENIENT);
 370         LocalDate parsed = LocalDate.parse(wby + "-" + week + "-" + dow.getValue(), f);
 371         assertEquals(parsed, date);
 372     }
 373 
 374     //-----------------------------------------------------------------------
 375     @DataProvider(name="parseLenientWeek")
 376     Object[][] data_parseLenientWeek() {
 377         return new Object[][] {
 378                 {"2012:52:-1", LocalDate.of(2012, 12, 22), false},
 379                 {"2012:52:0", LocalDate.of(2012, 12, 23), false},
 380                 {"2012:52:8", LocalDate.of(2012, 12, 31), false},
 381                 {"2012:52:9", LocalDate.of(2013, 1, 1), false},
 382 
 383                 {"2012:53:1", LocalDate.of(2012, 12, 31), true},
 384                 {"2012:54:1", LocalDate.of(2013, 1, 7), false},
 385 
 386                 {"2013:0:1", LocalDate.of(2012, 12, 24), false},
 387                 {"2013:0:0", LocalDate.of(2012, 12, 23), false},
 388         };
 389     }
 390 
 391     @Test(dataProvider = "parseLenientWeek", expectedExceptions = DateTimeParseException.class)
 392     public void test_parse_parseLenientWeek_STRICT(String str, LocalDate expected, boolean smart) {
 393         DateTimeFormatter f = new DateTimeFormatterBuilder()
 394                 .appendValue(IsoFields.WEEK_BASED_YEAR).appendLiteral(':')
 395                 .appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR).appendLiteral(':')
 396                 .appendValue(DAY_OF_WEEK)
 397                 .toFormatter().withResolverStyle(ResolverStyle.STRICT);
 398         LocalDate.parse(str, f);
 399     }
 400 
 401     @Test(dataProvider = "parseLenientWeek")
 402     public void test_parse_parseLenientWeek_SMART(String str, LocalDate expected, boolean smart) {
 403         DateTimeFormatter f = new DateTimeFormatterBuilder()
 404                 .appendValue(IsoFields.WEEK_BASED_YEAR).appendLiteral(':')
 405                 .appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR).appendLiteral(':')
 406                 .appendValue(DAY_OF_WEEK)
 407                 .toFormatter().withResolverStyle(ResolverStyle.SMART);
 408         if (smart) {
 409             LocalDate parsed = LocalDate.parse(str, f);
 410             assertEquals(parsed, expected);
 411         } else {
 412             try {
 413                 LocalDate.parse(str, f);
 414                 fail("Should have failed");
 415             } catch (DateTimeParseException ex) {
 416                 // expected
 417             }
 418         }
 419     }
 420 
 421     @Test(dataProvider = "parseLenientWeek")
 422     public void test_parse_parseLenientWeek_LENIENT(String str, LocalDate expected, boolean smart) {
 423         DateTimeFormatter f = new DateTimeFormatterBuilder()
 424                 .appendValue(IsoFields.WEEK_BASED_YEAR).appendLiteral(':')
 425                 .appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR).appendLiteral(':')
 426                 .appendValue(DAY_OF_WEEK)
 427                 .toFormatter().withResolverStyle(ResolverStyle.LENIENT);
 428         LocalDate parsed = LocalDate.parse(str, f);
 429         assertEquals(parsed, expected);
 430     }
 431 
 432     //-----------------------------------------------------------------------
 433     public void test_loop() {
 434         // loop round at least one 400 year cycle, including before 1970
 435         LocalDate date = LocalDate.of(1960, 1, 5);  // Tuseday of week 1 1960
 436         int year = 1960;
 437         int wby = 1960;
 438         int weekLen = 52;
 439         int week = 1;
 440         while (date.getYear() < 2400) {
 441             DayOfWeek loopDow = date.getDayOfWeek();
 442             if (date.getYear() != year) {
 443                 year = date.getYear();
 444             }
 445             if (loopDow == MONDAY) {
 446                 week++;
 447                 if ((week == 53 && weekLen == 52) || week == 54) {
 448                     week = 1;
 449                     LocalDate firstDayOfWeekBasedYear = date.plusDays(14).withDayOfYear(1);
 450                     DayOfWeek firstDay = firstDayOfWeekBasedYear.getDayOfWeek();
 451                     weekLen = (firstDay == THURSDAY || (firstDay == WEDNESDAY && firstDayOfWeekBasedYear.isLeapYear()) ? 53 : 52);
 452                     wby++;
 453                 }
 454             }
 455             assertEquals(IsoFields.WEEK_OF_WEEK_BASED_YEAR.rangeRefinedBy(date), ValueRange.of(1, weekLen), "Failed on " + date + " " + date.getDayOfWeek());
 456             assertEquals(IsoFields.WEEK_OF_WEEK_BASED_YEAR.getFrom(date), week, "Failed on " + date + " " + date.getDayOfWeek());
 457             assertEquals(date.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR), week, "Failed on " + date + " " + date.getDayOfWeek());
 458             assertEquals(IsoFields.WEEK_BASED_YEAR.getFrom(date), wby, "Failed on " + date + " " + date.getDayOfWeek());
 459             assertEquals(date.get(IsoFields.WEEK_BASED_YEAR), wby, "Failed on " + date + " " + date.getDayOfWeek());
 460             date = date.plusDays(1);
 461         }
 462     }
 463 
 464     // TODO: more tests
 465 }