1 /* 2 * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos 3 * 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * * Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * * Neither the name of JSR-310 nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package org.threeten.bp.chrono; 33 34 import static org.testng.Assert.assertEquals; 35 import static org.testng.Assert.assertTrue; 36 37 import java.io.ByteArrayInputStream; 38 import java.io.ByteArrayOutputStream; 39 import java.io.ObjectInputStream; 40 import java.io.ObjectOutputStream; 41 import java.util.ArrayList; 42 import java.util.List; 43 import java.util.Locale; 44 import java.util.Map; 45 46 import org.testng.Assert; 47 import org.testng.annotations.DataProvider; 48 import org.testng.annotations.Test; 49 import org.threeten.bp.Duration; 50 import org.threeten.bp.LocalDate; 51 import org.threeten.bp.LocalDateTime; 52 import org.threeten.bp.LocalTime; 53 import org.threeten.bp.format.ResolverStyle; 54 import org.threeten.bp.temporal.ChronoUnit; 55 import org.threeten.bp.temporal.Temporal; 56 import org.threeten.bp.temporal.TemporalAccessor; 57 import org.threeten.bp.temporal.TemporalAdjuster; 58 import org.threeten.bp.temporal.TemporalAmount; 59 import org.threeten.bp.temporal.TemporalField; 60 import org.threeten.bp.temporal.TemporalUnit; 61 import org.threeten.bp.temporal.ValueRange; 62 63 /** 64 * Test assertions that must be true for all built-in chronologies. 65 */ 66 @SuppressWarnings("rawtypes") 67 @Test 68 public class TestChronoLocalDateTime { 69 //----------------------------------------------------------------------- 70 // regular data factory for names and descriptions of available calendars 71 //----------------------------------------------------------------------- 72 @DataProvider(name = "calendars") data_of_calendars()73 Chronology[][] data_of_calendars() { 74 return new Chronology[][]{ 75 {HijrahChronology.INSTANCE}, 76 {IsoChronology.INSTANCE}, 77 {JapaneseChronology.INSTANCE}, 78 {MinguoChronology.INSTANCE}, 79 {ThaiBuddhistChronology.INSTANCE}}; 80 } 81 82 @Test(dataProvider="calendars") test_badWithAdjusterChrono(Chronology chrono)83 public void test_badWithAdjusterChrono(Chronology chrono) { 84 LocalDate refDate = LocalDate.of(1900, 1, 1); 85 ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); 86 for (Chronology[] clist : data_of_calendars()) { 87 Chronology chrono2 = clist[0]; 88 ChronoLocalDateTime<?> cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); 89 TemporalAdjuster adjuster = new FixedAdjuster(cdt2); 90 if (chrono != chrono2) { 91 try { 92 cdt.with(adjuster); 93 Assert.fail("WithAdjuster should have thrown a ClassCastException, " 94 + "required: " + cdt + ", supplied: " + cdt2); 95 } catch (ClassCastException cce) { 96 // Expected exception; not an error 97 } 98 } else { 99 // Same chronology, 100 ChronoLocalDateTime<?> result = cdt.with(adjuster); 101 assertEquals(result, cdt2, "WithAdjuster failed to replace date"); 102 } 103 } 104 } 105 106 @Test(dataProvider="calendars") test_badPlusAdjusterChrono(Chronology chrono)107 public void test_badPlusAdjusterChrono(Chronology chrono) { 108 LocalDate refDate = LocalDate.of(1900, 1, 1); 109 ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); 110 for (Chronology[] clist : data_of_calendars()) { 111 Chronology chrono2 = clist[0]; 112 ChronoLocalDateTime<?> cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); 113 TemporalAmount adjuster = new FixedAdjuster(cdt2); 114 if (chrono != chrono2) { 115 try { 116 cdt.plus(adjuster); 117 Assert.fail("WithAdjuster should have thrown a ClassCastException, " 118 + "required: " + cdt + ", supplied: " + cdt2); 119 } catch (ClassCastException cce) { 120 // Expected exception; not an error 121 } 122 } else { 123 // Same chronology, 124 ChronoLocalDateTime<?> result = cdt.plus(adjuster); 125 assertEquals(result, cdt2, "WithAdjuster failed to replace date time"); 126 } 127 } 128 } 129 130 @Test(dataProvider="calendars") test_badMinusAdjusterChrono(Chronology chrono)131 public void test_badMinusAdjusterChrono(Chronology chrono) { 132 LocalDate refDate = LocalDate.of(1900, 1, 1); 133 ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); 134 for (Chronology[] clist : data_of_calendars()) { 135 Chronology chrono2 = clist[0]; 136 ChronoLocalDateTime<?> cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); 137 TemporalAmount adjuster = new FixedAdjuster(cdt2); 138 if (chrono != chrono2) { 139 try { 140 cdt.minus(adjuster); 141 Assert.fail("WithAdjuster should have thrown a ClassCastException, " 142 + "required: " + cdt + ", supplied: " + cdt2); 143 } catch (ClassCastException cce) { 144 // Expected exception; not an error 145 } 146 } else { 147 // Same chronology, 148 ChronoLocalDateTime<?> result = cdt.minus(adjuster); 149 assertEquals(result, cdt2, "WithAdjuster failed to replace date"); 150 } 151 } 152 } 153 154 @Test(dataProvider="calendars") test_badPlusPeriodUnitChrono(Chronology chrono)155 public void test_badPlusPeriodUnitChrono(Chronology chrono) { 156 LocalDate refDate = LocalDate.of(1900, 1, 1); 157 ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); 158 for (Chronology[] clist : data_of_calendars()) { 159 Chronology chrono2 = clist[0]; 160 ChronoLocalDateTime<?> cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); 161 TemporalUnit adjuster = new FixedPeriodUnit(cdt2); 162 if (chrono != chrono2) { 163 try { 164 cdt.plus(1, adjuster); 165 Assert.fail("PeriodUnit.doAdd plus should have thrown a ClassCastException" + cdt 166 + ", can not be cast to " + cdt2); 167 } catch (ClassCastException cce) { 168 // Expected exception; not an error 169 } 170 } else { 171 // Same chronology, 172 ChronoLocalDateTime<?> result = cdt.plus(1, adjuster); 173 assertEquals(result, cdt2, "WithAdjuster failed to replace date"); 174 } 175 } 176 } 177 178 @Test(dataProvider="calendars") test_badMinusPeriodUnitChrono(Chronology chrono)179 public void test_badMinusPeriodUnitChrono(Chronology chrono) { 180 LocalDate refDate = LocalDate.of(1900, 1, 1); 181 ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); 182 for (Chronology[] clist : data_of_calendars()) { 183 Chronology chrono2 = clist[0]; 184 ChronoLocalDateTime<?> cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); 185 TemporalUnit adjuster = new FixedPeriodUnit(cdt2); 186 if (chrono != chrono2) { 187 try { 188 cdt.minus(1, adjuster); 189 Assert.fail("PeriodUnit.doAdd minus should have thrown a ClassCastException" + cdt.getClass() 190 + ", can not be cast to " + cdt2.getClass()); 191 } catch (ClassCastException cce) { 192 // Expected exception; not an error 193 } 194 } else { 195 // Same chronology, 196 ChronoLocalDateTime<?> result = cdt.minus(1, adjuster); 197 assertEquals(result, cdt2, "WithAdjuster failed to replace date"); 198 } 199 } 200 } 201 202 @Test(dataProvider="calendars") test_badDateTimeFieldChrono(Chronology chrono)203 public void test_badDateTimeFieldChrono(Chronology chrono) { 204 LocalDate refDate = LocalDate.of(1900, 1, 1); 205 ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); 206 for (Chronology[] clist : data_of_calendars()) { 207 Chronology chrono2 = clist[0]; 208 ChronoLocalDateTime<?> cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); 209 TemporalField adjuster = new FixedDateTimeField(cdt2); 210 if (chrono != chrono2) { 211 try { 212 cdt.with(adjuster, 1); 213 Assert.fail("DateTimeField doSet should have thrown a ClassCastException" + cdt.getClass() 214 + ", can not be cast to " + cdt2.getClass()); 215 } catch (ClassCastException cce) { 216 // Expected exception; not an error 217 } 218 } else { 219 // Same chronology, 220 ChronoLocalDateTime<?> result = cdt.with(adjuster, 1); 221 assertEquals(result, cdt2, "DateTimeField doSet failed to replace date"); 222 } 223 } 224 } 225 226 //----------------------------------------------------------------------- 227 // isBefore, isAfter, isEqual 228 //----------------------------------------------------------------------- 229 @Test(dataProvider="calendars") test_datetime_comparisons(Chronology chrono)230 public void test_datetime_comparisons(Chronology chrono) { 231 List<ChronoLocalDateTime<?>> dates = new ArrayList<ChronoLocalDateTime<?>>(); 232 233 ChronoLocalDateTime<?> date = chrono.date(LocalDate.of(1900, 1, 1)).atTime(LocalTime.MIN); 234 235 // Insert dates in order, no duplicates 236 if (chrono != JapaneseChronology.INSTANCE) { 237 dates.add(date.minus(100, ChronoUnit.YEARS)); 238 } 239 dates.add(date.minus(1, ChronoUnit.YEARS)); 240 dates.add(date.minus(1, ChronoUnit.MONTHS)); 241 dates.add(date.minus(1, ChronoUnit.WEEKS)); 242 dates.add(date.minus(1, ChronoUnit.DAYS)); 243 dates.add(date.minus(1, ChronoUnit.HOURS)); 244 dates.add(date.minus(1, ChronoUnit.MINUTES)); 245 dates.add(date.minus(1, ChronoUnit.SECONDS)); 246 dates.add(date.minus(1, ChronoUnit.NANOS)); 247 dates.add(date); 248 dates.add(date.plus(1, ChronoUnit.NANOS)); 249 dates.add(date.plus(1, ChronoUnit.SECONDS)); 250 dates.add(date.plus(1, ChronoUnit.MINUTES)); 251 dates.add(date.plus(1, ChronoUnit.HOURS)); 252 dates.add(date.plus(1, ChronoUnit.DAYS)); 253 dates.add(date.plus(1, ChronoUnit.WEEKS)); 254 dates.add(date.plus(1, ChronoUnit.MONTHS)); 255 dates.add(date.plus(1, ChronoUnit.YEARS)); 256 dates.add(date.plus(100, ChronoUnit.YEARS)); 257 258 // Check these dates against the corresponding dates for every calendar 259 for (Chronology[] clist : data_of_calendars()) { 260 List<ChronoLocalDateTime<?>> otherDates = new ArrayList<ChronoLocalDateTime<?>>(); 261 Chronology chrono2 = clist[0]; 262 if (chrono2 == JapaneseChronology.INSTANCE) { 263 continue; 264 } 265 for (ChronoLocalDateTime<?> d : dates) { 266 otherDates.add(chrono2.date(d).atTime(d.toLocalTime())); 267 } 268 269 // Now compare the sequence of original dates with the sequence of converted dates 270 for (int i = 0; i < dates.size(); i++) { 271 ChronoLocalDateTime<?> a = dates.get(i); 272 for (int j = 0; j < otherDates.size(); j++) { 273 ChronoLocalDateTime<?> b = otherDates.get(j); 274 int cmp = ChronoLocalDateTime.timeLineOrder().compare(a, b); 275 if (i < j) { 276 assertTrue(cmp < 0, a + " compare " + b); 277 assertEquals(a.isBefore(b), true, a + " isBefore " + b); 278 assertEquals(a.isAfter(b), false, a + " isAfter " + b); 279 assertEquals(a.isEqual(b), false, a + " isEqual " + b); 280 } else if (i > j) { 281 assertTrue(cmp > 0, a + " compare " + b); 282 assertEquals(a.isBefore(b), false, a + " isBefore " + b); 283 assertEquals(a.isAfter(b), true, a + " isAfter " + b); 284 assertEquals(a.isEqual(b), false, a + " isEqual " + b); 285 } else { 286 assertTrue(cmp == 0, a + " compare " + b); 287 assertEquals(a.isBefore(b), false, a + " isBefore " + b); 288 assertEquals(a.isAfter(b), false, a + " isAfter " + b); 289 assertEquals(a.isEqual(b), true, a + " isEqual " + b); 290 } 291 } 292 } 293 } 294 } 295 296 //----------------------------------------------------------------------- 297 // Test Serialization of ISO via chrono API 298 //----------------------------------------------------------------------- 299 @Test( dataProvider="calendars") test_ChronoLocalDateTimeSerialization(Chronology chrono)300 public void test_ChronoLocalDateTimeSerialization(Chronology chrono) throws Exception { 301 LocalDateTime ref = LocalDate.of(2000, 1, 5).atTime(12, 1, 2, 3); 302 ChronoLocalDateTime<?> orginal = chrono.date(ref).atTime(ref.toLocalTime()); 303 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 304 ObjectOutputStream out = new ObjectOutputStream(baos); 305 out.writeObject(orginal); 306 out.close(); 307 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 308 ObjectInputStream in = new ObjectInputStream(bais); 309 ChronoLocalDateTime<?> ser = (ChronoLocalDateTime<?>) in.readObject(); 310 assertEquals(ser, orginal, "deserialized date is wrong"); 311 } 312 313 314 /** 315 * FixedAdjusted returns a fixed DateTime in all adjustments. 316 * Construct an adjuster with the DateTime that should be returned. 317 */ 318 static class FixedAdjuster implements TemporalAdjuster, TemporalAmount { 319 private Temporal datetime; 320 FixedAdjuster(Temporal datetime)321 FixedAdjuster(Temporal datetime) { 322 this.datetime = datetime; 323 } 324 325 @Override adjustInto(Temporal ignore)326 public Temporal adjustInto(Temporal ignore) { 327 return datetime; 328 } 329 330 @Override addTo(Temporal ignore)331 public Temporal addTo(Temporal ignore) { 332 return datetime; 333 } 334 335 @Override subtractFrom(Temporal ignore)336 public Temporal subtractFrom(Temporal ignore) { 337 return datetime; 338 } 339 340 @Override getUnits()341 public List<TemporalUnit> getUnits() { 342 throw new UnsupportedOperationException("Not supported yet."); 343 } 344 345 @Override get(TemporalUnit unit)346 public long get(TemporalUnit unit) { 347 throw new UnsupportedOperationException("Not supported yet."); 348 } 349 } 350 351 /** 352 * FixedPeriodUnit returns a fixed DateTime in all adjustments. 353 * Construct an FixedPeriodUnit with the DateTime that should be returned. 354 */ 355 static class FixedPeriodUnit implements TemporalUnit { 356 private Temporal dateTime; 357 FixedPeriodUnit(Temporal dateTime)358 FixedPeriodUnit(Temporal dateTime) { 359 this.dateTime = dateTime; 360 } 361 362 @Override toString()363 public String toString() { 364 return "FixedPeriodUnit"; 365 } 366 367 @Override getDuration()368 public Duration getDuration() { 369 throw new UnsupportedOperationException("Not supported yet."); 370 } 371 372 @Override isDurationEstimated()373 public boolean isDurationEstimated() { 374 throw new UnsupportedOperationException("Not supported yet."); 375 } 376 377 @Override isDateBased()378 public boolean isDateBased() { 379 throw new UnsupportedOperationException("Not supported yet."); 380 } 381 382 @Override isTimeBased()383 public boolean isTimeBased() { 384 throw new UnsupportedOperationException("Not supported yet."); 385 } 386 387 @Override isSupportedBy(Temporal dateTime)388 public boolean isSupportedBy(Temporal dateTime) { 389 throw new UnsupportedOperationException("Not supported yet."); 390 } 391 392 @SuppressWarnings("unchecked") 393 @Override addTo(R dateTime, long periodToAdd)394 public <R extends Temporal> R addTo(R dateTime, long periodToAdd) { 395 return (R) this.dateTime; 396 } 397 398 @Override between(Temporal temporal1, Temporal temporal2)399 public long between(Temporal temporal1, Temporal temporal2) { 400 throw new UnsupportedOperationException("Not supported yet."); 401 } 402 } 403 404 /** 405 * FixedDateTimeField returns a fixed DateTime in all adjustments. 406 * Construct an FixedDateTimeField with the DateTime that should be returned from doSet. 407 */ 408 static class FixedDateTimeField implements TemporalField { 409 private Temporal dateTime; FixedDateTimeField(Temporal dateTime)410 FixedDateTimeField(Temporal dateTime) { 411 this.dateTime = dateTime; 412 } 413 414 @Override toString()415 public String toString() { 416 return "FixedDateTimeField"; 417 } 418 419 @Override getBaseUnit()420 public TemporalUnit getBaseUnit() { 421 throw new UnsupportedOperationException("Not supported yet."); 422 } 423 424 @Override getRangeUnit()425 public TemporalUnit getRangeUnit() { 426 throw new UnsupportedOperationException("Not supported yet."); 427 } 428 429 @Override range()430 public ValueRange range() { 431 throw new UnsupportedOperationException("Not supported yet."); 432 } 433 434 @Override isDateBased()435 public boolean isDateBased() { 436 throw new UnsupportedOperationException("Not supported yet."); 437 } 438 439 @Override isTimeBased()440 public boolean isTimeBased() { 441 throw new UnsupportedOperationException("Not supported yet."); 442 } 443 444 @Override isSupportedBy(TemporalAccessor dateTime)445 public boolean isSupportedBy(TemporalAccessor dateTime) { 446 throw new UnsupportedOperationException("Not supported yet."); 447 } 448 449 @Override rangeRefinedBy(TemporalAccessor dateTime)450 public ValueRange rangeRefinedBy(TemporalAccessor dateTime) { 451 throw new UnsupportedOperationException("Not supported yet."); 452 } 453 454 @Override getFrom(TemporalAccessor dateTime)455 public long getFrom(TemporalAccessor dateTime) { 456 throw new UnsupportedOperationException("Not supported yet."); 457 } 458 459 @SuppressWarnings("unchecked") 460 @Override adjustInto(R dateTime, long newValue)461 public <R extends Temporal> R adjustInto(R dateTime, long newValue) { 462 return (R) this.dateTime; 463 } 464 465 @Override getDisplayName(Locale locale)466 public String getDisplayName(Locale locale) { 467 throw new UnsupportedOperationException("Not supported yet."); 468 } 469 470 @Override resolve(Map<TemporalField, Long> fieldValues, TemporalAccessor partialTemporal, ResolverStyle resolverStyle)471 public TemporalAccessor resolve(Map<TemporalField, Long> fieldValues, 472 TemporalAccessor partialTemporal, ResolverStyle resolverStyle) { 473 return null; 474 } 475 } 476 } 477