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.chrono; 58 59 import static org.testng.Assert.assertEquals; 60 import static org.testng.Assert.assertTrue; 61 62 import java.io.ByteArrayInputStream; 63 import java.io.ByteArrayOutputStream; 64 import java.io.ObjectInputStream; 65 import java.io.ObjectOutputStream; 66 import java.time.Duration; 67 import java.time.LocalDate; 68 import java.time.LocalDateTime; 69 import java.time.LocalTime; 70 import java.time.chrono.HijrahChronology; 71 import java.time.chrono.JapaneseChronology; 72 import java.time.chrono.MinguoChronology; 73 import java.time.chrono.ThaiBuddhistChronology; 74 import java.time.chrono.ChronoLocalDate; 75 import java.time.chrono.ChronoLocalDateTime; 76 import java.time.chrono.Chronology; 77 import java.time.chrono.IsoChronology; 78 import java.time.temporal.ChronoUnit; 79 import java.time.temporal.Temporal; 80 import java.time.temporal.TemporalAccessor; 81 import java.time.temporal.TemporalAdjuster; 82 import java.time.temporal.TemporalAmount; 83 import java.time.temporal.TemporalField; 84 import java.time.temporal.TemporalUnit; 85 import java.time.temporal.ValueRange; 86 import java.util.ArrayList; 87 import java.util.List; 88 89 import org.testng.Assert; 90 import org.testng.annotations.DataProvider; 91 import org.testng.annotations.Test; 92 93 /** 94 * Test assertions that must be true for all built-in chronologies. 95 */ 96 @Test 97 public class TestChronoLocalDateTime { 98 //----------------------------------------------------------------------- 99 // regular data factory for names and descriptions of available calendars 100 //----------------------------------------------------------------------- 101 @DataProvider(name = "calendars") 102 Chronology[][] data_of_calendars() { 103 return new Chronology[][]{ 104 {HijrahChronology.INSTANCE}, 105 {IsoChronology.INSTANCE}, 106 {JapaneseChronology.INSTANCE}, 107 {MinguoChronology.INSTANCE}, 108 {ThaiBuddhistChronology.INSTANCE}}; 109 } 110 111 @Test(groups={"tck"}, dataProvider="calendars") 112 public void test_badWithAdjusterChrono(Chronology chrono) { 113 LocalDate refDate = LocalDate.of(1900, 1, 1); 114 ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); 115 for (Chronology[] clist : data_of_calendars()) { 116 Chronology chrono2 = clist[0]; 117 ChronoLocalDateTime<?> cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); 118 TemporalAdjuster adjuster = new FixedAdjuster(cdt2); 119 if (chrono != chrono2) { 120 try { 121 ChronoLocalDateTime<?> notreached = cdt.with(adjuster); 122 Assert.fail("WithAdjuster should have thrown a ClassCastException, " 123 + "required: " + cdt + ", supplied: " + cdt2); 124 } catch (ClassCastException cce) { 125 // Expected exception; not an error 126 } 127 } else { 128 // Same chronology, 129 ChronoLocalDateTime<?> result = cdt.with(adjuster); 130 assertEquals(result, cdt2, "WithAdjuster failed to replace date"); 131 } 132 } 133 } 134 135 @Test(groups={"tck"}, dataProvider="calendars") 136 public void test_badPlusAdjusterChrono(Chronology chrono) { 137 LocalDate refDate = LocalDate.of(1900, 1, 1); 138 ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); 139 for (Chronology[] clist : data_of_calendars()) { 140 Chronology chrono2 = clist[0]; 141 ChronoLocalDateTime<?> cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); 142 TemporalAmount adjuster = new FixedAdjuster(cdt2); 143 if (chrono != chrono2) { 144 try { 145 ChronoLocalDateTime<?> notreached = cdt.plus(adjuster); 146 Assert.fail("WithAdjuster should have thrown a ClassCastException, " 147 + "required: " + cdt + ", supplied: " + cdt2); 148 } catch (ClassCastException cce) { 149 // Expected exception; not an error 150 } 151 } else { 152 // Same chronology, 153 ChronoLocalDateTime<?> result = cdt.plus(adjuster); 154 assertEquals(result, cdt2, "WithAdjuster failed to replace date time"); 155 } 156 } 157 } 158 159 @Test(groups={"tck"}, dataProvider="calendars") 160 public void test_badMinusAdjusterChrono(Chronology chrono) { 161 LocalDate refDate = LocalDate.of(1900, 1, 1); 162 ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); 163 for (Chronology[] clist : data_of_calendars()) { 164 Chronology chrono2 = clist[0]; 165 ChronoLocalDateTime<?> cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); 166 TemporalAmount adjuster = new FixedAdjuster(cdt2); 167 if (chrono != chrono2) { 168 try { 169 ChronoLocalDateTime<?> notreached = cdt.minus(adjuster); 170 Assert.fail("WithAdjuster should have thrown a ClassCastException, " 171 + "required: " + cdt + ", supplied: " + cdt2); 172 } catch (ClassCastException cce) { 173 // Expected exception; not an error 174 } 175 } else { 176 // Same chronology, 177 ChronoLocalDateTime<?> result = cdt.minus(adjuster); 178 assertEquals(result, cdt2, "WithAdjuster failed to replace date"); 179 } 180 } 181 } 182 183 @Test(groups={"tck"}, dataProvider="calendars") 184 public void test_badPlusTemporalUnitChrono(Chronology chrono) { 185 LocalDate refDate = LocalDate.of(1900, 1, 1); 186 ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); 187 for (Chronology[] clist : data_of_calendars()) { 188 Chronology chrono2 = clist[0]; 189 ChronoLocalDateTime<?> cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); 190 TemporalUnit adjuster = new FixedTemporalUnit(cdt2); 191 if (chrono != chrono2) { 192 try { 193 ChronoLocalDateTime<?> notreached = cdt.plus(1, adjuster); 194 Assert.fail("TemporalUnit.doAdd plus should have thrown a ClassCastException" + cdt 195 + ", can not be cast to " + cdt2); 196 } catch (ClassCastException cce) { 197 // Expected exception; not an error 198 } 199 } else { 200 // Same chronology, 201 ChronoLocalDateTime<?> result = cdt.plus(1, adjuster); 202 assertEquals(result, cdt2, "WithAdjuster failed to replace date"); 203 } 204 } 205 } 206 207 @Test(groups={"tck"}, dataProvider="calendars") 208 public void test_badMinusTemporalUnitChrono(Chronology chrono) { 209 LocalDate refDate = LocalDate.of(1900, 1, 1); 210 ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); 211 for (Chronology[] clist : data_of_calendars()) { 212 Chronology chrono2 = clist[0]; 213 ChronoLocalDateTime<?> cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); 214 TemporalUnit adjuster = new FixedTemporalUnit(cdt2); 215 if (chrono != chrono2) { 216 try { 217 ChronoLocalDateTime<?> notreached = cdt.minus(1, adjuster); 218 Assert.fail("TemporalUnit.doAdd minus should have thrown a ClassCastException" + cdt.getClass() 219 + ", can not be cast to " + cdt2.getClass()); 220 } catch (ClassCastException cce) { 221 // Expected exception; not an error 222 } 223 } else { 224 // Same chronology, 225 ChronoLocalDateTime<?> result = cdt.minus(1, adjuster); 226 assertEquals(result, cdt2, "WithAdjuster failed to replace date"); 227 } 228 } 229 } 230 231 @Test(groups={"tck"}, dataProvider="calendars") 232 public void test_badTemporalFieldChrono(Chronology chrono) { 233 LocalDate refDate = LocalDate.of(1900, 1, 1); 234 ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); 235 for (Chronology[] clist : data_of_calendars()) { 236 Chronology chrono2 = clist[0]; 237 ChronoLocalDateTime<?> cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); 238 TemporalField adjuster = new FixedTemporalField(cdt2); 239 if (chrono != chrono2) { 240 try { 241 ChronoLocalDateTime<?> notreached = cdt.with(adjuster, 1); 242 Assert.fail("TemporalField doSet should have thrown a ClassCastException" + cdt.getClass() 243 + ", can not be cast to " + cdt2.getClass()); 244 } catch (ClassCastException cce) { 245 // Expected exception; not an error 246 } 247 } else { 248 // Same chronology, 249 ChronoLocalDateTime<?> result = cdt.with(adjuster, 1); 250 assertEquals(result, cdt2, "TemporalField doSet failed to replace date"); 251 } 252 } 253 } 254 255 //----------------------------------------------------------------------- 256 // isBefore, isAfter, isEqual 257 //----------------------------------------------------------------------- 258 @Test(groups={"tck"}, dataProvider="calendars") 259 public void test_datetime_comparisons(Chronology chrono) { 260 List<ChronoLocalDateTime<?>> dates = new ArrayList<>(); 261 262 ChronoLocalDateTime<?> date = chrono.date(LocalDate.of(1900, 1, 1)).atTime(LocalTime.MIN); 263 264 // Insert dates in order, no duplicates 265 dates.add(date.minus(100, ChronoUnit.YEARS)); 266 dates.add(date.minus(1, ChronoUnit.YEARS)); 267 dates.add(date.minus(1, ChronoUnit.MONTHS)); 268 dates.add(date.minus(1, ChronoUnit.WEEKS)); 269 dates.add(date.minus(1, ChronoUnit.DAYS)); 270 dates.add(date.minus(1, ChronoUnit.HOURS)); 271 dates.add(date.minus(1, ChronoUnit.MINUTES)); 272 dates.add(date.minus(1, ChronoUnit.SECONDS)); 273 dates.add(date.minus(1, ChronoUnit.NANOS)); 274 dates.add(date); 275 dates.add(date.plus(1, ChronoUnit.NANOS)); 276 dates.add(date.plus(1, ChronoUnit.SECONDS)); 277 dates.add(date.plus(1, ChronoUnit.MINUTES)); 278 dates.add(date.plus(1, ChronoUnit.HOURS)); 279 dates.add(date.plus(1, ChronoUnit.DAYS)); 280 dates.add(date.plus(1, ChronoUnit.WEEKS)); 281 dates.add(date.plus(1, ChronoUnit.MONTHS)); 282 dates.add(date.plus(1, ChronoUnit.YEARS)); 283 dates.add(date.plus(100, ChronoUnit.YEARS)); 284 285 // Check these dates against the corresponding dates for every calendar 286 for (Chronology[] clist : data_of_calendars()) { 287 List<ChronoLocalDateTime<?>> otherDates = new ArrayList<>(); 288 Chronology chrono2 = clist[0]; 289 for (ChronoLocalDateTime<?> d : dates) { 290 otherDates.add(chrono2.date(d).atTime(d.toLocalTime())); 291 } 292 293 // Now compare the sequence of original dates with the sequence of converted dates 294 for (int i = 0; i < dates.size(); i++) { 295 ChronoLocalDateTime<?> a = dates.get(i); 296 for (int j = 0; j < otherDates.size(); j++) { 297 ChronoLocalDateTime<?> b = otherDates.get(j); 298 int cmp = ChronoLocalDateTime.DATE_TIME_COMPARATOR.compare(a, b); 299 if (i < j) { 300 assertTrue(cmp < 0, a + " compare " + b); 301 assertEquals(a.isBefore(b), true, a + " isBefore " + b); 302 assertEquals(a.isAfter(b), false, a + " isAfter " + b); 303 assertEquals(a.isEqual(b), false, a + " isEqual " + b); 304 } else if (i > j) { 305 assertTrue(cmp > 0, a + " compare " + b); 306 assertEquals(a.isBefore(b), false, a + " isBefore " + b); 307 assertEquals(a.isAfter(b), true, a + " isAfter " + b); 308 assertEquals(a.isEqual(b), false, a + " isEqual " + b); 309 } else { 310 assertTrue(cmp == 0, a + " compare " + b); 311 assertEquals(a.isBefore(b), false, a + " isBefore " + b); 312 assertEquals(a.isAfter(b), false, a + " isAfter " + b); 313 assertEquals(a.isEqual(b), true, a + " isEqual " + b); 314 } 315 } 316 } 317 } 318 } 319 320 //----------------------------------------------------------------------- 321 // Test Serialization of ISO via chrono API 322 //----------------------------------------------------------------------- 323 @Test( groups={"tck"}, dataProvider="calendars") 324 public void test_ChronoLocalDateTimeSerialization(Chronology chrono) throws Exception { 325 LocalDateTime ref = LocalDate.of(2000, 1, 5).atTime(12, 1, 2, 3); 326 ChronoLocalDateTime<?> orginal = chrono.date(ref).atTime(ref.toLocalTime()); 327 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 328 ObjectOutputStream out = new ObjectOutputStream(baos); 329 out.writeObject(orginal); 330 out.close(); 331 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 332 ObjectInputStream in = new ObjectInputStream(bais); 333 ChronoLocalDateTime<?> ser = (ChronoLocalDateTime<?>) in.readObject(); 334 assertEquals(ser, orginal, "deserialized date is wrong"); 335 } 336 337 338 /** 339 * FixedAdjusted returns a fixed Temporal in all adjustments. 340 * Construct an adjuster with the Temporal that should be returned from adjust. 341 */ 342 static class FixedAdjuster implements TemporalAdjuster, TemporalAmount { 343 private Temporal datetime; 344 345 FixedAdjuster(Temporal datetime) { 346 this.datetime = datetime; 347 } 348 349 @Override 350 public Temporal adjustInto(Temporal ignore) { 351 return datetime; 352 } 353 354 @Override 355 public Temporal addTo(Temporal ignore) { 356 return datetime; 357 } 358 359 @Override 360 public Temporal subtractFrom(Temporal ignore) { 361 return datetime; 362 } 363 364 @Override 365 public long get(TemporalUnit unit) { 366 throw new UnsupportedOperationException("Not supported yet."); 367 } 368 369 @Override 370 public List<TemporalUnit> getUnits() { 371 throw new UnsupportedOperationException("Not supported yet."); 372 } 373 374 } 375 376 /** 377 * FixedTemporalUnit returns a fixed Temporal in all adjustments. 378 * Construct an FixedTemporalUnit with the Temporal that should be returned from doAdd. 379 */ 380 static class FixedTemporalUnit implements TemporalUnit { 381 private Temporal temporal; 382 383 FixedTemporalUnit(Temporal temporal) { 384 this.temporal = temporal; 385 } 386 387 @Override 388 public String getName() { 389 return "FixedTemporalUnit"; 390 } 391 392 @Override 393 public Duration getDuration() { 394 throw new UnsupportedOperationException("Not supported yet."); 395 } 396 397 @Override 398 public boolean isDurationEstimated() { 399 throw new UnsupportedOperationException("Not supported yet."); 400 } 401 402 @Override 403 public boolean isSupportedBy(Temporal temporal) { 404 throw new UnsupportedOperationException("Not supported yet."); 405 } 406 407 @SuppressWarnings("unchecked") 408 @Override 409 public <R extends Temporal> R addTo(R temporal, long amount) { 410 return (R) this.temporal; 411 } 412 413 @Override 414 public long between(Temporal temporal1, Temporal temporal2) { 415 throw new UnsupportedOperationException("Not supported yet."); 416 } 417 } 418 419 /** 420 * FixedTemporalField returns a fixed Temporal in all adjustments. 421 * Construct an FixedTemporalField with the Temporal that should be returned from doSet. 422 */ 423 static class FixedTemporalField implements TemporalField { 424 private Temporal temporal; 425 FixedTemporalField(Temporal temporal) { 426 this.temporal = temporal; 427 } 428 429 @Override 430 public String getName() { 431 return "FixedTemporalField"; 432 } 433 434 @Override 435 public TemporalUnit getBaseUnit() { 436 throw new UnsupportedOperationException("Not supported yet."); 437 } 438 439 @Override 440 public TemporalUnit getRangeUnit() { 441 throw new UnsupportedOperationException("Not supported yet."); 442 } 443 444 @Override 445 public ValueRange range() { 446 throw new UnsupportedOperationException("Not supported yet."); 447 } 448 449 @Override 450 public boolean isSupportedBy(TemporalAccessor temporal) { 451 throw new UnsupportedOperationException("Not supported yet."); 452 } 453 454 @Override 455 public ValueRange rangeRefinedBy(TemporalAccessor temporal) { 456 throw new UnsupportedOperationException("Not supported yet."); 457 } 458 459 @Override 460 public long getFrom(TemporalAccessor temporal) { 461 throw new UnsupportedOperationException("Not supported yet."); 462 } 463 464 @SuppressWarnings("unchecked") 465 @Override 466 public <R extends Temporal> R adjustInto(R temporal, long newValue) { 467 return (R) this.temporal; 468 } 469 } 470 }