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