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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * This file is available under and governed by the GNU General Public
26 * License version 2 only, as published by the Free Software Foundation.
27 * However, the following notice accompanied the original version of this
28 * file:
29 *
30 * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos
31 *
32 * All rights reserved.
33 *
34 * Redistribution and use in source and binary forms, with or without
35 * modification, are permitted provided that the following conditions are met:
36 *
37 *  * Redistributions of source code must retain the above copyright notice,
38 *    this list of conditions and the following disclaimer.
39 *
40 *  * Redistributions in binary form must reproduce the above copyright notice,
41 *    this list of conditions and the following disclaimer in the documentation
42 *    and/or other materials provided with the distribution.
43 *
44 *  * Neither the name of JSR-310 nor the names of its contributors
45 *    may be used to endorse or promote products derived from this software
46 *    without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
49 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
50 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
51 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
52 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
53 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
54 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
55 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
56 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
57 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
58 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59 */
60package tck.java.time;
61
62import static org.testng.Assert.assertEquals;
63import static org.testng.Assert.fail;
64
65import java.io.ByteArrayInputStream;
66import java.io.ByteArrayOutputStream;
67import java.io.DataOutputStream;
68import java.io.ObjectInputStream;
69import java.io.ObjectStreamConstants;
70import java.lang.reflect.Field;
71import java.time.DateTimeException;
72import java.time.Instant;
73import java.time.LocalTime;
74import java.time.ZoneId;
75import java.time.ZoneOffset;
76import java.time.format.TextStyle;
77import java.time.temporal.TemporalAccessor;
78import java.time.temporal.TemporalField;
79import java.time.temporal.TemporalQueries;
80import java.time.temporal.TemporalQuery;
81import java.time.zone.ZoneRulesException;
82import java.util.HashMap;
83import java.util.Locale;
84import java.util.Map;
85import java.util.Set;
86
87import org.testng.annotations.DataProvider;
88import org.testng.annotations.Test;
89
90/**
91 * Test ZoneId.
92 */
93@Test
94public class TCKZoneId extends AbstractTCKTest {
95
96    //-----------------------------------------------------------------------
97    // SHORT_IDS
98    //-----------------------------------------------------------------------
99    public void test_constant_OLD_IDS_POST_2005() {
100        Map<String, String> ids = ZoneId.SHORT_IDS;
101        assertEquals(ids.get("EST"), "-05:00");
102        assertEquals(ids.get("MST"), "-07:00");
103        assertEquals(ids.get("HST"), "-10:00");
104        assertEquals(ids.get("ACT"), "Australia/Darwin");
105        assertEquals(ids.get("AET"), "Australia/Sydney");
106        assertEquals(ids.get("AGT"), "America/Argentina/Buenos_Aires");
107        assertEquals(ids.get("ART"), "Africa/Cairo");
108        assertEquals(ids.get("AST"), "America/Anchorage");
109        assertEquals(ids.get("BET"), "America/Sao_Paulo");
110        assertEquals(ids.get("BST"), "Asia/Dhaka");
111        assertEquals(ids.get("CAT"), "Africa/Harare");
112        assertEquals(ids.get("CNT"), "America/St_Johns");
113        assertEquals(ids.get("CST"), "America/Chicago");
114        assertEquals(ids.get("CTT"), "Asia/Shanghai");
115        assertEquals(ids.get("EAT"), "Africa/Addis_Ababa");
116        assertEquals(ids.get("ECT"), "Europe/Paris");
117        assertEquals(ids.get("IET"), "America/Indiana/Indianapolis");
118        assertEquals(ids.get("IST"), "Asia/Kolkata");
119        assertEquals(ids.get("JST"), "Asia/Tokyo");
120        assertEquals(ids.get("MIT"), "Pacific/Apia");
121        assertEquals(ids.get("NET"), "Asia/Yerevan");
122        assertEquals(ids.get("NST"), "Pacific/Auckland");
123        assertEquals(ids.get("PLT"), "Asia/Karachi");
124        assertEquals(ids.get("PNT"), "America/Phoenix");
125        assertEquals(ids.get("PRT"), "America/Puerto_Rico");
126        assertEquals(ids.get("PST"), "America/Los_Angeles");
127        assertEquals(ids.get("SST"), "Pacific/Guadalcanal");
128        assertEquals(ids.get("VST"), "Asia/Ho_Chi_Minh");
129    }
130
131    @Test(expectedExceptions=UnsupportedOperationException.class)
132    public void test_constant_OLD_IDS_POST_2005_immutable() {
133        Map<String, String> ids = ZoneId.SHORT_IDS;
134        ids.clear();
135    }
136
137    //-----------------------------------------------------------------------
138    // getAvailableZoneIds()
139    //-----------------------------------------------------------------------
140    @Test
141    public void test_getAvailableGroupIds() {
142        Set<String> zoneIds = ZoneId.getAvailableZoneIds();
143        assertEquals(zoneIds.contains("Europe/London"), true);
144        zoneIds.clear();
145        assertEquals(zoneIds.size(), 0);
146        Set<String> zoneIds2 = ZoneId.getAvailableZoneIds();
147        assertEquals(zoneIds2.contains("Europe/London"), true);
148    }
149
150    //-----------------------------------------------------------------------
151    // mapped factory
152    //-----------------------------------------------------------------------
153    @Test
154    public void test_of_string_Map() {
155        Map<String, String> map = new HashMap<>();
156        map.put("LONDON", "Europe/London");
157        map.put("PARIS", "Europe/Paris");
158        ZoneId test = ZoneId.of("LONDON", map);
159        assertEquals(test.getId(), "Europe/London");
160    }
161
162    @Test
163    public void test_of_string_Map_lookThrough() {
164        Map<String, String> map = new HashMap<>();
165        map.put("LONDON", "Europe/London");
166        map.put("PARIS", "Europe/Paris");
167        ZoneId test = ZoneId.of("Europe/Madrid", map);
168        assertEquals(test.getId(), "Europe/Madrid");
169    }
170
171    @Test
172    public void test_of_string_Map_emptyMap() {
173        Map<String, String> map = new HashMap<>();
174        ZoneId test = ZoneId.of("Europe/Madrid", map);
175        assertEquals(test.getId(), "Europe/Madrid");
176    }
177
178    @Test(expectedExceptions=DateTimeException.class)
179    public void test_of_string_Map_badFormat() {
180        Map<String, String> map = new HashMap<>();
181        ZoneId.of("Not known", map);
182    }
183
184    @Test(expectedExceptions=ZoneRulesException.class)
185    public void test_of_string_Map_unknown() {
186        Map<String, String> map = new HashMap<>();
187        ZoneId.of("Unknown", map);
188    }
189
190    //-----------------------------------------------------------------------
191    // regular factory and .normalized()
192    //-----------------------------------------------------------------------
193    @DataProvider(name="offsetBasedValid")
194    Object[][] data_offsetBasedValid() {
195        return new Object[][] {
196                {"Z", "Z"},
197                {"+0", "Z"},
198                {"-0", "Z"},
199                {"+00", "Z"},
200                {"+0000", "Z"},
201                {"+00:00", "Z"},
202                {"+000000", "Z"},
203                {"+00:00:00", "Z"},
204                {"-00", "Z"},
205                {"-0000", "Z"},
206                {"-00:00", "Z"},
207                {"-000000", "Z"},
208                {"-00:00:00", "Z"},
209                {"+5", "+05:00"},
210                {"+01", "+01:00"},
211                {"+0100", "+01:00"},
212                {"+01:00", "+01:00"},
213                {"+010000", "+01:00"},
214                {"+01:00:00", "+01:00"},
215                {"+12", "+12:00"},
216                {"+1234", "+12:34"},
217                {"+12:34", "+12:34"},
218                {"+123456", "+12:34:56"},
219                {"+12:34:56", "+12:34:56"},
220                {"-02", "-02:00"},
221                {"-5", "-05:00"},
222                {"-0200", "-02:00"},
223                {"-02:00", "-02:00"},
224                {"-020000", "-02:00"},
225                {"-02:00:00", "-02:00"},
226        };
227    }
228
229    @Test(dataProvider="offsetBasedValid")
230    public void factory_of_String_offsetBasedValid_noPrefix(String input, String id) {
231        ZoneId test = ZoneId.of(input);
232        assertEquals(test.getId(), id);
233        assertEquals(test, ZoneOffset.of(id));
234        assertEquals(test.normalized(), ZoneOffset.of(id));
235        assertEquals(test.getDisplayName(TextStyle.FULL, Locale.UK), id);
236        assertEquals(test.getRules().isFixedOffset(), true);
237        assertEquals(test.getRules().getOffset(Instant.EPOCH), ZoneOffset.of(id));
238    }
239
240    //-----------------------------------------------------------------------
241    @DataProvider(name="offsetBasedValidPrefix")
242    Object[][] data_offsetBasedValidPrefix() {
243        return new Object[][] {
244                {"", "", "Z"},
245                {"+0", "", "Z"},
246                {"-0", "", "Z"},
247                {"+00", "", "Z"},
248                {"+0000", "", "Z"},
249                {"+00:00", "", "Z"},
250                {"+000000", "", "Z"},
251                {"+00:00:00", "", "Z"},
252                {"-00", "", "Z"},
253                {"-0000", "", "Z"},
254                {"-00:00", "", "Z"},
255                {"-000000", "", "Z"},
256                {"-00:00:00", "", "Z"},
257                {"+5", "+05:00", "+05:00"},
258                {"+01", "+01:00", "+01:00"},
259                {"+0100", "+01:00", "+01:00"},
260                {"+01:00", "+01:00", "+01:00"},
261                {"+010000", "+01:00", "+01:00"},
262                {"+01:00:00", "+01:00", "+01:00"},
263                {"+12", "+12:00", "+12:00"},
264                {"+1234", "+12:34", "+12:34"},
265                {"+12:34", "+12:34", "+12:34"},
266                {"+123456", "+12:34:56", "+12:34:56"},
267                {"+12:34:56", "+12:34:56", "+12:34:56"},
268                {"-02", "-02:00", "-02:00"},
269                {"-5", "-05:00", "-05:00"},
270                {"-0200", "-02:00", "-02:00"},
271                {"-02:00", "-02:00", "-02:00"},
272                {"-020000", "-02:00", "-02:00"},
273                {"-02:00:00", "-02:00", "-02:00"},
274        };
275    }
276
277    @Test(dataProvider="offsetBasedValidPrefix")
278    public void factory_of_String_offsetBasedValid_prefixUTC(String input, String id, String offsetId) {
279        ZoneId test = ZoneId.of("UTC" + input);
280        assertEquals(test.getId(), "UTC" + id);
281        assertEquals(test.getRules(), ZoneOffset.of(offsetId).getRules());
282        assertEquals(test.normalized(), ZoneOffset.of(offsetId));
283        assertEquals(test.getDisplayName(TextStyle.FULL, Locale.UK), displayName("UTC" + id));
284        assertEquals(test.getRules().isFixedOffset(), true);
285        assertEquals(test.getRules().getOffset(Instant.EPOCH), ZoneOffset.of(offsetId));
286    }
287
288    @Test(dataProvider="offsetBasedValidPrefix")
289    public void factory_of_String_offsetBasedValid_prefixGMT(String input, String id, String offsetId) {
290        ZoneId test = ZoneId.of("GMT" + input);
291        assertEquals(test.getId(), "GMT" + id);
292        assertEquals(test.getRules(), ZoneOffset.of(offsetId).getRules());
293        assertEquals(test.normalized(), ZoneOffset.of(offsetId));
294        assertEquals(test.getDisplayName(TextStyle.FULL, Locale.UK), displayName("GMT" + id));
295        assertEquals(test.getRules().isFixedOffset(), true);
296        assertEquals(test.getRules().getOffset(Instant.EPOCH), ZoneOffset.of(offsetId));
297    }
298
299    @Test(dataProvider="offsetBasedValidPrefix")
300    public void factory_of_String_offsetBasedValid_prefixUT(String input, String id, String offsetId) {
301        ZoneId test = ZoneId.of("UT" + input);
302        assertEquals(test.getId(), "UT" + id);
303        assertEquals(test.getRules(), ZoneOffset.of(offsetId).getRules());
304        assertEquals(test.normalized(), ZoneOffset.of(offsetId));
305        assertEquals(test.getDisplayName(TextStyle.FULL, Locale.UK), displayName("UT" + id));
306        assertEquals(test.getRules().isFixedOffset(), true);
307        assertEquals(test.getRules().getOffset(Instant.EPOCH), ZoneOffset.of(offsetId));
308    }
309
310    private String displayName(String id) {
311        // Android-changed: Android doesn't have long names for GMT/UTC as of 2017.
312//        if (id.equals("GMT")) {
313//            return "Greenwich Mean Time";
314//        }
315//        if (id.equals("GMT0")) {
316//            return "Greenwich Mean Time";
317//        }
318//        if (id.equals("UTC")) {
319//            return "Coordinated Universal Time";
320//        }
321        return id;
322    }
323
324    //-----------------------------------------------------------------------
325    @DataProvider(name="prefixValid")
326    Object[][] data_prefixValid() {
327        return new Object[][] {
328                {"GMT", "+01:00"},
329                {"UTC", "+01:00"},
330                {"UT", "+01:00"},
331                {"", "+01:00"},
332        };
333    }
334
335    @Test(dataProvider="prefixValid")
336    public void test_prefixOfOffset(String prefix, String offset) {
337        ZoneOffset zoff = ZoneOffset.of(offset);
338        ZoneId zoneId = ZoneId.ofOffset(prefix, zoff);
339        assertEquals(zoneId.getId(), prefix + zoff.getId(), "in correct id for : " + prefix + ", zoff: " + zoff);
340
341    }
342
343    //-----------------------------------------------------------------------
344    @DataProvider(name="prefixInvalid")
345    Object[][] data_prefixInvalid() {
346        return new Object[][] {
347                {"GM", "+01:00"},
348                {"U", "+01:00"},
349                {"UTC0", "+01:00"},
350                {"A", "+01:00"},
351        };
352    }
353
354    @Test(dataProvider="prefixInvalid", expectedExceptions=java.lang.IllegalArgumentException.class)
355    public void test_invalidPrefixOfOffset(String prefix, String offset) {
356        ZoneOffset zoff = ZoneOffset.of(offset);
357        ZoneId zoneId = ZoneId.ofOffset(prefix, zoff);
358        fail("should have thrown an exception for prefix: " + prefix);
359    }
360
361    @Test(expectedExceptions=java.lang.NullPointerException.class)
362    public void test_nullPrefixOfOffset() {
363        ZoneId.ofOffset(null, ZoneOffset.ofTotalSeconds(1));
364    }
365
366    @Test(expectedExceptions=java.lang.NullPointerException.class)
367    public void test_nullOffsetOfOffset() {
368        ZoneId.ofOffset("GMT", null);
369    }
370
371    //-----------------------------------------------------------------------
372    @DataProvider(name="offsetBasedValidOther")
373    Object[][] data_offsetBasedValidOther() {
374        return new Object[][] {
375                {"GMT", "Z"},
376                {"GMT0", "Z"},
377                {"UCT", "Z"},
378                {"Greenwich", "Z"},
379                {"Universal", "Z"},
380                {"Zulu", "Z"},
381                {"Etc/GMT", "Z"},
382                {"Etc/GMT+0", "Z"},
383                {"Etc/GMT+1", "-01:00"},
384                {"Etc/GMT-1", "+01:00"},
385                {"Etc/GMT+9", "-09:00"},
386                {"Etc/GMT-9", "+09:00"},
387                {"Etc/GMT0", "Z"},
388                {"Etc/UCT", "Z"},
389                {"Etc/UTC", "Z"},
390                {"Etc/Greenwich", "Z"},
391                {"Etc/Universal", "Z"},
392                {"Etc/Zulu", "Z"},
393        };
394    }
395
396    @Test(dataProvider="offsetBasedValidOther")
397    public void factory_of_String_offsetBasedValidOther(String input, String offsetId) {
398        ZoneId test = ZoneId.of(input);
399        assertEquals(test.getId(), input);
400        assertEquals(test.getRules(), ZoneOffset.of(offsetId).getRules());
401        assertEquals(test.normalized(), ZoneOffset.of(offsetId));
402    }
403
404    //-----------------------------------------------------------------------
405    @DataProvider(name="offsetBasedInvalid")
406    Object[][] data_offsetBasedInvalid() {
407        return new Object[][] {
408                {"A"}, {"B"}, {"C"}, {"D"}, {"E"}, {"F"}, {"G"}, {"H"}, {"I"}, {"J"}, {"K"}, {"L"}, {"M"},
409                {"N"}, {"O"}, {"P"}, {"Q"}, {"R"}, {"S"}, {"T"}, {"U"}, {"V"}, {"W"}, {"X"}, {"Y"}, {"Z"},
410                {"+0:00"}, {"+00:0"}, {"+0:0"},
411                {"+000"}, {"+00000"},
412                {"+0:00:00"}, {"+00:0:00"}, {"+00:00:0"}, {"+0:0:0"}, {"+0:0:00"}, {"+00:0:0"}, {"+0:00:0"},
413                {"+01_00"}, {"+01;00"}, {"+01@00"}, {"+01:AA"},
414                {"+19"}, {"+19:00"}, {"+18:01"}, {"+18:00:01"}, {"+1801"}, {"+180001"},
415                {"-0:00"}, {"-00:0"}, {"-0:0"},
416                {"-000"}, {"-00000"},
417                {"-0:00:00"}, {"-00:0:00"}, {"-00:00:0"}, {"-0:0:0"}, {"-0:0:00"}, {"-00:0:0"}, {"-0:00:0"},
418                {"-19"}, {"-19:00"}, {"-18:01"}, {"-18:00:01"}, {"-1801"}, {"-180001"},
419                {"-01_00"}, {"-01;00"}, {"-01@00"}, {"-01:AA"},
420                {"@01:00"},
421                {"0"},
422                {"UT0"},
423                {"UTZ"},
424                {"UTC0"},
425                {"UTCZ"},
426                {"GMTZ"},  // GMT0 is valid in ZoneRulesProvider
427        };
428    }
429
430    @Test(dataProvider="offsetBasedInvalid", expectedExceptions=DateTimeException.class)
431    public void factory_of_String_offsetBasedInvalid_noPrefix(String id) {
432        if (id.equals("Z")) {
433            throw new DateTimeException("Fake exception: Z alone is valid, not invalid");
434        }
435        ZoneId.of(id);
436    }
437
438    @Test(dataProvider="offsetBasedInvalid", expectedExceptions=DateTimeException.class)
439    public void factory_of_String_offsetBasedInvalid_prefixUTC(String id) {
440        ZoneId.of("UTC" + id);
441    }
442
443    @Test(dataProvider="offsetBasedInvalid", expectedExceptions=DateTimeException.class)
444    public void factory_of_String_offsetBasedInvalid_prefixGMT(String id) {
445        if (id.equals("0")) {
446            throw new DateTimeException("Fake exception: GMT0 is valid, not invalid");
447        }
448        ZoneId.of("GMT" + id);
449    }
450
451    @Test(dataProvider="offsetBasedInvalid", expectedExceptions=DateTimeException.class)
452    public void factory_of_String_offsetBasedInvalid_prefixUT(String id) {
453        if (id.equals("C")) {
454            throw new DateTimeException("Fake exception: UT + C = UTC, thus it is valid, not invalid");
455        }
456        ZoneId.of("UT" + id);
457    }
458
459    //-----------------------------------------------------------------------
460    @DataProvider(name="regionBasedInvalid")
461    Object[][] data_regionBasedInvalid() {
462        // \u00ef is a random unicode character
463        return new Object[][] {
464                {""}, {":"}, {"#"},
465                {"\u00ef"}, {"`"}, {"!"}, {"\""}, {"\u00ef"}, {"$"}, {"^"}, {"&"}, {"*"}, {"("}, {")"}, {"="},
466                {"\\"}, {"|"}, {","}, {"<"}, {">"}, {"?"}, {";"}, {"'"}, {"["}, {"]"}, {"{"}, {"}"},
467                {"\u00ef:A"}, {"`:A"}, {"!:A"}, {"\":A"}, {"\u00ef:A"}, {"$:A"}, {"^:A"}, {"&:A"}, {"*:A"}, {"(:A"}, {"):A"}, {"=:A"}, {"+:A"},
468                {"\\:A"}, {"|:A"}, {",:A"}, {"<:A"}, {">:A"}, {"?:A"}, {";:A"}, {"::A"}, {"':A"}, {"@:A"}, {"~:A"}, {"[:A"}, {"]:A"}, {"{:A"}, {"}:A"},
469                {"A:B#\u00ef"}, {"A:B#`"}, {"A:B#!"}, {"A:B#\""}, {"A:B#\u00ef"}, {"A:B#$"}, {"A:B#^"}, {"A:B#&"}, {"A:B#*"},
470                {"A:B#("}, {"A:B#)"}, {"A:B#="}, {"A:B#+"},
471                {"A:B#\\"}, {"A:B#|"}, {"A:B#,"}, {"A:B#<"}, {"A:B#>"}, {"A:B#?"}, {"A:B#;"}, {"A:B#:"},
472                {"A:B#'"}, {"A:B#@"}, {"A:B#~"}, {"A:B#["}, {"A:B#]"}, {"A:B#{"}, {"A:B#}"},
473        };
474    }
475
476    @Test(dataProvider="regionBasedInvalid", expectedExceptions=DateTimeException.class)
477    public void factory_of_String_regionBasedInvalid(String id) {
478        ZoneId.of(id);
479    }
480
481    //-----------------------------------------------------------------------
482    @Test
483    public void factory_of_String_region_EuropeLondon() {
484        ZoneId test = ZoneId.of("Europe/London");
485        assertEquals(test.getId(), "Europe/London");
486        assertEquals(test.getRules().isFixedOffset(), false);
487        assertEquals(test.normalized(), test);
488    }
489
490    //-----------------------------------------------------------------------
491    @Test(expectedExceptions=NullPointerException.class)
492    public void factory_of_String_null() {
493        ZoneId.of(null);
494    }
495
496    @Test(expectedExceptions=DateTimeException.class)
497    public void factory_of_String_badFormat() {
498        ZoneId.of("Unknown rule");
499    }
500
501    @Test(expectedExceptions=ZoneRulesException.class)
502    public void factory_of_String_unknown() {
503        ZoneId.of("Unknown");
504    }
505
506    //-----------------------------------------------------------------------
507    // from(TemporalAccessor)
508    //-----------------------------------------------------------------------
509    @Test
510    public void factory_from_TemporalAccessor_zoneId() {
511        TemporalAccessor mock = new TemporalAccessor() {
512            @Override
513            public boolean isSupported(TemporalField field) {
514                return false;
515            }
516            @Override
517            public long getLong(TemporalField field) {
518                throw new DateTimeException("Mock");
519            }
520            @SuppressWarnings("unchecked")
521            @Override
522            public <R> R query(TemporalQuery<R> query) {
523                if (query == TemporalQueries.zoneId()) {
524                    return (R) ZoneId.of("Europe/Paris");
525                }
526                return TemporalAccessor.super.query(query);
527            }
528        };
529        assertEquals(ZoneId.from(mock),  ZoneId.of("Europe/Paris"));
530    }
531
532    @Test
533    public void factory_from_TemporalAccessor_offset() {
534        ZoneOffset offset = ZoneOffset.ofHours(1);
535        assertEquals(ZoneId.from(offset), offset);
536    }
537
538    @Test(expectedExceptions=DateTimeException.class)
539    public void factory_from_TemporalAccessor_invalid_noDerive() {
540        ZoneId.from(LocalTime.of(12, 30));
541    }
542
543    @Test(expectedExceptions=NullPointerException.class)
544    public void factory_from_TemporalAccessor_null() {
545        ZoneId.from(null);
546    }
547
548    //-----------------------------------------------------------------------
549    // equals() / hashCode()
550    //-----------------------------------------------------------------------
551    @Test
552    public void test_equals() {
553        ZoneId test1 = ZoneId.of("Europe/London");
554        ZoneId test2 = ZoneId.of("Europe/Paris");
555        ZoneId test2b = ZoneId.of("Europe/Paris");
556        assertEquals(test1.equals(test2), false);
557        assertEquals(test2.equals(test1), false);
558
559        assertEquals(test1.equals(test1), true);
560        assertEquals(test2.equals(test2), true);
561        assertEquals(test2.equals(test2b), true);
562
563        assertEquals(test1.hashCode() == test1.hashCode(), true);
564        assertEquals(test2.hashCode() == test2.hashCode(), true);
565        assertEquals(test2.hashCode() == test2b.hashCode(), true);
566    }
567
568    @Test
569    public void test_equals_null() {
570        assertEquals(ZoneId.of("Europe/London").equals(null), false);
571    }
572
573    @Test
574    public void test_equals_notEqualWrongType() {
575        assertEquals(ZoneId.of("Europe/London").equals("Europe/London"), false);
576    }
577
578    //-----------------------------------------------------------------------
579    // toString()
580    //-----------------------------------------------------------------------
581    @DataProvider(name="toString")
582    Object[][] data_toString() {
583        return new Object[][] {
584                {"Europe/London", "Europe/London"},
585                {"Europe/Paris", "Europe/Paris"},
586                {"Europe/Berlin", "Europe/Berlin"},
587                {"Z", "Z"},
588                {"+01:00", "+01:00"},
589                {"UTC", "UTC"},
590                {"UTC+01:00", "UTC+01:00"},
591        };
592    }
593
594    @Test(dataProvider="toString")
595    public void test_toString(String id, String expected) {
596        ZoneId test = ZoneId.of(id);
597        assertEquals(test.toString(), expected);
598    }
599
600}
601