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 */
57package tck.java.time.chrono;
58
59import static org.testng.Assert.assertEquals;
60import static org.testng.Assert.assertTrue;
61
62import java.io.ByteArrayInputStream;
63import java.io.ByteArrayOutputStream;
64import java.io.ObjectInputStream;
65import java.io.ObjectOutputStream;
66import java.time.DateTimeException;
67import java.time.Duration;
68import java.time.LocalDate;
69import java.time.LocalTime;
70import java.time.ZoneId;
71import java.time.ZoneOffset;
72import java.time.ZonedDateTime;
73import java.time.chrono.ChronoZonedDateTime;
74import java.time.chrono.Chronology;
75import java.time.chrono.HijrahChronology;
76import java.time.chrono.IsoChronology;
77import java.time.chrono.JapaneseChronology;
78import java.time.chrono.MinguoChronology;
79import java.time.chrono.ThaiBuddhistChronology;
80import java.time.temporal.ChronoUnit;
81import java.time.temporal.Temporal;
82import java.time.temporal.TemporalAccessor;
83import java.time.temporal.TemporalAdjuster;
84import java.time.temporal.TemporalAmount;
85import java.time.temporal.TemporalField;
86import java.time.temporal.TemporalUnit;
87import java.time.temporal.ValueRange;
88import java.util.ArrayList;
89import java.util.List;
90
91import org.testng.Assert;
92import org.testng.annotations.DataProvider;
93import org.testng.annotations.Test;
94
95/**
96 * Test assertions that must be true for all built-in chronologies.
97 */
98@Test
99public class TCKChronoZonedDateTime {
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(dataProvider="calendars")
116    public void test_badWithAdjusterChrono(Chronology chrono) {
117        LocalDate refDate = LocalDate.of(2013, 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(dataProvider="calendars")
139    public void test_badPlusAdjusterChrono(Chronology chrono) {
140        LocalDate refDate = LocalDate.of(2013, 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(dataProvider="calendars")
163    public void test_badMinusAdjusterChrono(Chronology chrono) {
164        LocalDate refDate = LocalDate.of(2013, 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(dataProvider="calendars")
187    public void test_badPlusTemporalUnitChrono(Chronology chrono) {
188        LocalDate refDate = LocalDate.of(2013, 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(dataProvider="calendars")
211    public void test_badMinusTemporalUnitChrono(Chronology chrono) {
212        LocalDate refDate = LocalDate.of(2013, 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(dataProvider="calendars")
235    public void test_badTemporalFieldChrono(Chronology chrono) {
236        LocalDate refDate = LocalDate.of(2013, 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, timeLineOrder()  test a Chronology against the other Chronos
260    //-----------------------------------------------------------------------
261    @Test(dataProvider="calendars")
262    public void test_zonedDateTime_comparisons(Chronology chrono) {
263        List<ChronoZonedDateTime<?>> dates = new ArrayList<>();
264
265        ChronoZonedDateTime<?> date = chrono.date(LocalDate.of(2013, 1, 1))
266                .atTime(LocalTime.MIN)
267                .atZone(ZoneOffset.UTC);
268
269        // Insert dates in order, no duplicates
270        dates.add(date.minus(1, ChronoUnit.YEARS));
271        dates.add(date.minus(1, ChronoUnit.MONTHS));
272        dates.add(date.minus(1, ChronoUnit.WEEKS));
273        dates.add(date.minus(1, ChronoUnit.DAYS));
274        dates.add(date.minus(1, ChronoUnit.HOURS));
275        dates.add(date.minus(1, ChronoUnit.MINUTES));
276        dates.add(date.minus(1, ChronoUnit.SECONDS));
277        dates.add(date.minus(1, ChronoUnit.NANOS));
278        dates.add(date);
279        dates.add(date.plus(1, ChronoUnit.NANOS));
280        dates.add(date.plus(1, ChronoUnit.SECONDS));
281        dates.add(date.plus(1, ChronoUnit.MINUTES));
282        dates.add(date.plus(1, ChronoUnit.HOURS));
283        dates.add(date.plus(1, ChronoUnit.DAYS));
284        dates.add(date.plus(1, ChronoUnit.WEEKS));
285        dates.add(date.plus(1, ChronoUnit.MONTHS));
286        dates.add(date.plus(1, ChronoUnit.YEARS));
287
288        // Check these dates against the corresponding dates for every calendar
289        for (Chronology[] clist : data_of_calendars()) {
290            List<ChronoZonedDateTime<?>> otherDates = new ArrayList<>();
291            Chronology chrono2 = IsoChronology.INSTANCE; //clist[0];
292            for (ChronoZonedDateTime<?> d : dates) {
293                otherDates.add(chrono2.date(d).atTime(d.toLocalTime()).atZone(d.getZone()));
294            }
295
296            // Now compare  the sequence of original dates with the sequence of converted dates
297            for (int i = 0; i < dates.size(); i++) {
298                ChronoZonedDateTime<?> a = dates.get(i);
299                for (int j = 0; j < otherDates.size(); j++) {
300                    ChronoZonedDateTime<?> b = otherDates.get(j);
301                    int cmp = ChronoZonedDateTime.timeLineOrder().compare(a, b);
302                    if (i < j) {
303                        assertTrue(cmp < 0, a + " compare " + b);
304                        assertEquals(a.isBefore(b), true, a + " isBefore " + b);
305                        assertEquals(a.isAfter(b), false, a + " ifAfter " + b);
306                        assertEquals(a.isEqual(b), false, a + " isEqual " + b);
307                    } else if (i > j) {
308                        assertTrue(cmp > 0, a + " compare " + b);
309                        assertEquals(a.isBefore(b), false, a + " isBefore " + b);
310                        assertEquals(a.isAfter(b), true, a + " ifAfter " + b);
311                        assertEquals(a.isEqual(b), false, a + " isEqual " + b);
312                    } else {
313                        assertTrue(cmp == 0, a + " compare " + b);
314                        assertEquals(a.isBefore(b), false, a + " isBefore " + b);
315                        assertEquals(a.isAfter(b), false, a + " ifAfter " + b);
316                        assertEquals(a.isEqual(b), true, a + " isEqual " + b);
317                    }
318                }
319            }
320        }
321    }
322
323    //-----------------------------------------------------------------------
324    @Test(dataProvider="calendars")
325    public void test_from_TemporalAccessor(Chronology chrono) {
326        ZonedDateTime refDateTime = ZonedDateTime.of(2013, 1, 1, 12, 30, 0, 0, ZoneId.of("Europe/Paris"));
327        ChronoZonedDateTime<?> dateTime = chrono.zonedDateTime(refDateTime);
328        ChronoZonedDateTime<?> test1 = ChronoZonedDateTime.from(dateTime);
329        assertEquals(test1, dateTime);
330    }
331
332    @Test(expectedExceptions = DateTimeException.class)
333    public void test_from_TemporalAccessor_dateOnly() {
334        ChronoZonedDateTime.from(LocalDate.of(2013, 1, 1));
335    }
336
337    @Test(expectedExceptions = DateTimeException.class)
338    public void test_from_TemporalAccessor_timeOnly() {
339        ChronoZonedDateTime.from(LocalTime.of(12, 30));
340    }
341
342    @Test(expectedExceptions = NullPointerException.class)
343    public void test_from_TemporalAccessor_null() {
344        ChronoZonedDateTime.from(null);
345    }
346
347    //-----------------------------------------------------------------------
348    @Test(dataProvider="calendars")
349    public void test_getChronology(Chronology chrono) {
350        ChronoZonedDateTime<?> test = chrono.zonedDateTime(ZonedDateTime.of(2010, 6, 30, 11, 30, 0, 0, ZoneOffset.UTC));
351        assertEquals(test.getChronology(), chrono);
352    }
353
354    //-----------------------------------------------------------------------
355    /**
356     * FixedAdjusted returns a fixed Temporal in all adjustments.
357     * Construct an adjuster with the Temporal that should be returned from adjust.
358     */
359    static class FixedAdjuster implements TemporalAdjuster, TemporalAmount {
360        private Temporal datetime;
361
362        FixedAdjuster(Temporal datetime) {
363            this.datetime = datetime;
364        }
365
366        @Override
367        public Temporal adjustInto(Temporal ignore) {
368            return datetime;
369        }
370
371        @Override
372        public Temporal addTo(Temporal ignore) {
373            return datetime;
374        }
375
376        @Override
377        public Temporal subtractFrom(Temporal ignore) {
378            return datetime;
379        }
380
381        @Override
382        public long get(TemporalUnit unit) {
383            throw new UnsupportedOperationException("Not supported yet.");
384        }
385
386        @Override
387        public List<TemporalUnit> getUnits() {
388            throw new UnsupportedOperationException("Not supported yet.");
389        }
390
391    }
392
393    /**
394     * FixedTemporalUnit returns a fixed Temporal in all adjustments.
395     * Construct an FixedTemporalUnit with the Temporal that should be returned from addTo.
396     */
397    static class FixedTemporalUnit implements TemporalUnit {
398        private Temporal temporal;
399
400        FixedTemporalUnit(Temporal temporal) {
401            this.temporal = temporal;
402        }
403
404        @Override
405        public Duration getDuration() {
406            throw new UnsupportedOperationException("Not supported yet.");
407        }
408
409        @Override
410        public boolean isDurationEstimated() {
411            throw new UnsupportedOperationException("Not supported yet.");
412        }
413
414        @Override
415        public boolean isDateBased() {
416            return false;
417        }
418
419        @Override
420        public boolean isTimeBased() {
421            return false;
422        }
423
424        @Override
425        public boolean isSupportedBy(Temporal temporal) {
426            throw new UnsupportedOperationException("Not supported yet.");
427        }
428
429        @SuppressWarnings("unchecked")
430        @Override
431        public <R extends Temporal> R addTo(R temporal, long amount) {
432            return (R) this.temporal;
433        }
434
435        @Override
436        public long between(Temporal temporal1, Temporal temporal2) {
437            throw new UnsupportedOperationException("Not supported yet.");
438        }
439
440        @Override
441        public String toString() {
442            return "FixedTemporalUnit";
443        }
444
445    }
446
447    /**
448     * FixedTemporalField returns a fixed Temporal in all adjustments.
449     * Construct an FixedTemporalField with the Temporal that should be returned from adjustInto.
450     */
451    static class FixedTemporalField implements TemporalField {
452        private Temporal temporal;
453        FixedTemporalField(Temporal temporal) {
454            this.temporal = temporal;
455        }
456
457        @Override
458        public TemporalUnit getBaseUnit() {
459            throw new UnsupportedOperationException("Not supported yet.");
460        }
461
462        @Override
463        public TemporalUnit getRangeUnit() {
464            throw new UnsupportedOperationException("Not supported yet.");
465        }
466
467        @Override
468        public ValueRange range() {
469            throw new UnsupportedOperationException("Not supported yet.");
470        }
471
472        @Override
473        public boolean isDateBased() {
474            return false;
475        }
476
477        @Override
478        public boolean isTimeBased() {
479            return false;
480        }
481
482        @Override
483        public boolean isSupportedBy(TemporalAccessor temporal) {
484            throw new UnsupportedOperationException("Not supported yet.");
485        }
486
487        @Override
488        public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
489            throw new UnsupportedOperationException("Not supported yet.");
490        }
491
492        @Override
493        public long getFrom(TemporalAccessor temporal) {
494            throw new UnsupportedOperationException("Not supported yet.");
495        }
496
497        @SuppressWarnings("unchecked")
498        @Override
499        public <R extends Temporal> R adjustInto(R temporal, long newValue) {
500            return (R) this.temporal;
501        }
502
503        @Override
504        public String toString() {
505            return "FixedTemporalField";
506        }
507    }
508}
509