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