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