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  */
  60 package tck.java.time;
  61 
  62 import static org.testng.Assert.assertEquals;
  63 import static org.testng.Assert.fail;
  64 
  65 import java.io.ByteArrayInputStream;
  66 import java.io.ByteArrayOutputStream;
  67 import java.io.DataOutputStream;
  68 import java.io.ObjectInputStream;
  69 import java.io.ObjectStreamConstants;
  70 import java.lang.reflect.Field;
  71 import java.time.DateTimeException;
  72 import java.time.Instant;
  73 import java.time.LocalTime;
  74 import java.time.ZoneId;
  75 import java.time.ZoneOffset;
  76 import java.time.format.TextStyle;
  77 import java.time.temporal.TemporalAccessor;
  78 import java.time.temporal.TemporalField;
  79 import java.time.temporal.TemporalQuery;
  80 import java.time.zone.ZoneRulesException;
  81 import java.util.HashMap;
  82 import java.util.Locale;
  83 import java.util.Map;
  84 import java.util.Set;
  85 
  86 import org.testng.annotations.DataProvider;
  87 import org.testng.annotations.Test;
  88 
  89 /**
  90  * Test ZoneId.
  91  */
  92 @Test
  93 public class TCKZoneId extends AbstractTCKTest {
  94 
  95     //-----------------------------------------------------------------------
  96     @Test
  97     public void test_serialization() throws Exception {
  98         assertSerializable(ZoneId.of("Europe/London"));
  99         assertSerializable(ZoneId.of("America/Chicago"));
 100     }
 101 
 102     @Test
 103     public void test_serialization_format() throws Exception {
 104         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 105         try (DataOutputStream dos = new DataOutputStream(baos) ) {
 106             dos.writeByte(7);
 107             dos.writeUTF("Europe/London");
 108         }
 109         byte[] bytes = baos.toByteArray();
 110         assertSerializedBySer(ZoneId.of("Europe/London"), bytes);
 111     }
 112 
 113     @Test
 114     public void test_deserialization_lenient_characters() throws Exception {
 115         // an ID can be loaded without validation during deserialization
 116         String id = "QWERTYUIOPASDFGHJKLZXCVBNM~/._+-";
 117         ZoneId deser = deserialize(id);
 118         // getId, equals, hashCode, toString and normalized are OK
 119         assertEquals(deser.getId(), id);
 120         assertEquals(deser.toString(), id);
 121         assertEquals(deser, deser);
 122         assertEquals(deser.hashCode(), deser.hashCode());
 123         assertEquals(deser.normalized(), deser);
 124         // getting the rules is not
 125         try {
 126             deser.getRules();
 127             fail();
 128         } catch (ZoneRulesException ex) {
 129             // expected
 130         }
 131     }
 132 
 133     @Test(expectedExceptions=DateTimeException.class)
 134     public void test_deserialization_lenient_badCharacters() throws Exception {
 135         // an ID can be loaded without validation during deserialization
 136         // but there is a check to ensure the ID format is valid
 137         deserialize("|!?");
 138     }
 139 
 140     @Test(dataProvider="offsetBasedValid")
 141     public void test_deserialization_lenient_offsetNotAllowed_noPrefix(String input, String resolvedId) throws Exception {
 142         ZoneId deserialized = deserialize(input);
 143         assertEquals(deserialized, ZoneId.of(input));
 144         assertEquals(deserialized, ZoneId.of(resolvedId));
 145     }
 146 
 147     @Test(dataProvider="offsetBasedValidPrefix")
 148     public void test_deserialization_lenient_offsetNotAllowed_prefixUTC(String input, String resolvedId, String offsetId) throws Exception {
 149         ZoneId deserialized = deserialize("UTC" + input);
 150         assertEquals(deserialized, ZoneId.of("UTC" + input));
 151         assertEquals(deserialized, ZoneId.of("UTC" + resolvedId));
 152     }
 153 
 154     @Test(dataProvider="offsetBasedValidPrefix")
 155     public void test_deserialization_lenient_offsetNotAllowed_prefixGMT(String input, String resolvedId, String offsetId) throws Exception {
 156         ZoneId deserialized = deserialize("GMT" + input);
 157         assertEquals(deserialized, ZoneId.of("GMT" + input));
 158         assertEquals(deserialized, ZoneId.of("GMT" + resolvedId));
 159     }
 160 
 161     @Test(dataProvider="offsetBasedValidPrefix")
 162     public void test_deserialization_lenient_offsetNotAllowed_prefixUT(String input, String resolvedId, String offsetId) throws Exception {
 163         ZoneId deserialized = deserialize("UT" + input);
 164         assertEquals(deserialized, ZoneId.of("UT" + input));
 165         assertEquals(deserialized, ZoneId.of("UT" + resolvedId));
 166     }
 167 
 168     private ZoneId deserialize(String id) throws Exception {
 169         String serClass = ZoneId.class.getPackage().getName() + ".Ser";
 170         Class<?> serCls = Class.forName(serClass);
 171         Field field = serCls.getDeclaredField("serialVersionUID");
 172         field.setAccessible(true);
 173         long serVer = (Long) field.get(null);
 174         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 175         try (DataOutputStream dos = new DataOutputStream(baos)) {
 176             dos.writeShort(ObjectStreamConstants.STREAM_MAGIC);
 177             dos.writeShort(ObjectStreamConstants.STREAM_VERSION);
 178             dos.writeByte(ObjectStreamConstants.TC_OBJECT);
 179             dos.writeByte(ObjectStreamConstants.TC_CLASSDESC);
 180             dos.writeUTF(serClass);
 181             dos.writeLong(serVer);
 182             dos.writeByte(ObjectStreamConstants.SC_EXTERNALIZABLE | ObjectStreamConstants.SC_BLOCK_DATA);
 183             dos.writeShort(0);  // number of fields
 184             dos.writeByte(ObjectStreamConstants.TC_ENDBLOCKDATA);  // end of classdesc
 185             dos.writeByte(ObjectStreamConstants.TC_NULL);  // no superclasses
 186             dos.writeByte(ObjectStreamConstants.TC_BLOCKDATA);
 187             dos.writeByte(1 + 2 + id.length());  // length of data (1 byte + 2 bytes UTF length + 32 bytes UTF)
 188             dos.writeByte(7);  // ZoneId
 189             dos.writeUTF(id);
 190             dos.writeByte(ObjectStreamConstants.TC_ENDBLOCKDATA);  // end of blockdata
 191         }
 192         ZoneId deser = null;
 193         try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) {
 194             deser = (ZoneId) ois.readObject();
 195         }
 196         return deser;
 197     }
 198 
 199     //-----------------------------------------------------------------------
 200     // OLD_SHORT_IDS
 201     //-----------------------------------------------------------------------
 202     public void test_constant_OLD_IDS_PRE_2005() {
 203         Map<String, String> ids = ZoneId.OLD_SHORT_IDS;
 204         assertEquals(ids.get("EST"), "America/New_York");
 205         assertEquals(ids.get("MST"), "America/Denver");
 206         assertEquals(ids.get("HST"), "Pacific/Honolulu");
 207         assertEquals(ids.get("ACT"), "Australia/Darwin");
 208         assertEquals(ids.get("AET"), "Australia/Sydney");
 209         assertEquals(ids.get("AGT"), "America/Argentina/Buenos_Aires");
 210         assertEquals(ids.get("ART"), "Africa/Cairo");
 211         assertEquals(ids.get("AST"), "America/Anchorage");
 212         assertEquals(ids.get("BET"), "America/Sao_Paulo");
 213         assertEquals(ids.get("BST"), "Asia/Dhaka");
 214         assertEquals(ids.get("CAT"), "Africa/Harare");
 215         assertEquals(ids.get("CNT"), "America/St_Johns");
 216         assertEquals(ids.get("CST"), "America/Chicago");
 217         assertEquals(ids.get("CTT"), "Asia/Shanghai");
 218         assertEquals(ids.get("EAT"), "Africa/Addis_Ababa");
 219         assertEquals(ids.get("ECT"), "Europe/Paris");
 220         assertEquals(ids.get("IET"), "America/Indiana/Indianapolis");
 221         assertEquals(ids.get("IST"), "Asia/Kolkata");
 222         assertEquals(ids.get("JST"), "Asia/Tokyo");
 223         assertEquals(ids.get("MIT"), "Pacific/Apia");
 224         assertEquals(ids.get("NET"), "Asia/Yerevan");
 225         assertEquals(ids.get("NST"), "Pacific/Auckland");
 226         assertEquals(ids.get("PLT"), "Asia/Karachi");
 227         assertEquals(ids.get("PNT"), "America/Phoenix");
 228         assertEquals(ids.get("PRT"), "America/Puerto_Rico");
 229         assertEquals(ids.get("PST"), "America/Los_Angeles");
 230         assertEquals(ids.get("SST"), "Pacific/Guadalcanal");
 231         assertEquals(ids.get("VST"), "Asia/Ho_Chi_Minh");
 232     }
 233 
 234     @Test(expectedExceptions=UnsupportedOperationException.class)
 235     public void test_constant_OLD_IDS_PRE_2005_immutable() {
 236         Map<String, String> ids = ZoneId.OLD_SHORT_IDS;
 237         ids.clear();
 238     }
 239 
 240     //-----------------------------------------------------------------------
 241     // SHORT_IDS
 242     //-----------------------------------------------------------------------
 243     public void test_constant_OLD_IDS_POST_2005() {
 244         Map<String, String> ids = ZoneId.SHORT_IDS;
 245         assertEquals(ids.get("EST"), "-05:00");
 246         assertEquals(ids.get("MST"), "-07:00");
 247         assertEquals(ids.get("HST"), "-10:00");
 248         assertEquals(ids.get("ACT"), "Australia/Darwin");
 249         assertEquals(ids.get("AET"), "Australia/Sydney");
 250         assertEquals(ids.get("AGT"), "America/Argentina/Buenos_Aires");
 251         assertEquals(ids.get("ART"), "Africa/Cairo");
 252         assertEquals(ids.get("AST"), "America/Anchorage");
 253         assertEquals(ids.get("BET"), "America/Sao_Paulo");
 254         assertEquals(ids.get("BST"), "Asia/Dhaka");
 255         assertEquals(ids.get("CAT"), "Africa/Harare");
 256         assertEquals(ids.get("CNT"), "America/St_Johns");
 257         assertEquals(ids.get("CST"), "America/Chicago");
 258         assertEquals(ids.get("CTT"), "Asia/Shanghai");
 259         assertEquals(ids.get("EAT"), "Africa/Addis_Ababa");
 260         assertEquals(ids.get("ECT"), "Europe/Paris");
 261         assertEquals(ids.get("IET"), "America/Indiana/Indianapolis");
 262         assertEquals(ids.get("IST"), "Asia/Kolkata");
 263         assertEquals(ids.get("JST"), "Asia/Tokyo");
 264         assertEquals(ids.get("MIT"), "Pacific/Apia");
 265         assertEquals(ids.get("NET"), "Asia/Yerevan");
 266         assertEquals(ids.get("NST"), "Pacific/Auckland");
 267         assertEquals(ids.get("PLT"), "Asia/Karachi");
 268         assertEquals(ids.get("PNT"), "America/Phoenix");
 269         assertEquals(ids.get("PRT"), "America/Puerto_Rico");
 270         assertEquals(ids.get("PST"), "America/Los_Angeles");
 271         assertEquals(ids.get("SST"), "Pacific/Guadalcanal");
 272         assertEquals(ids.get("VST"), "Asia/Ho_Chi_Minh");
 273     }
 274 
 275     @Test(expectedExceptions=UnsupportedOperationException.class)
 276     public void test_constant_OLD_IDS_POST_2005_immutable() {
 277         Map<String, String> ids = ZoneId.SHORT_IDS;
 278         ids.clear();
 279     }
 280 
 281     //-----------------------------------------------------------------------
 282     // getAvailableZoneIds()
 283     //-----------------------------------------------------------------------
 284     @Test
 285     public void test_getAvailableGroupIds() {
 286         Set<String> zoneIds = ZoneId.getAvailableZoneIds();
 287         assertEquals(zoneIds.contains("Europe/London"), true);
 288         zoneIds.clear();
 289         assertEquals(zoneIds.size(), 0);
 290         Set<String> zoneIds2 = ZoneId.getAvailableZoneIds();
 291         assertEquals(zoneIds2.contains("Europe/London"), true);
 292     }
 293 
 294     //-----------------------------------------------------------------------
 295     // mapped factory
 296     //-----------------------------------------------------------------------
 297     @Test
 298     public void test_of_string_Map() {
 299         Map<String, String> map = new HashMap<>();
 300         map.put("LONDON", "Europe/London");
 301         map.put("PARIS", "Europe/Paris");
 302         ZoneId test = ZoneId.of("LONDON", map);
 303         assertEquals(test.getId(), "Europe/London");
 304     }
 305 
 306     @Test
 307     public void test_of_string_Map_lookThrough() {
 308         Map<String, String> map = new HashMap<>();
 309         map.put("LONDON", "Europe/London");
 310         map.put("PARIS", "Europe/Paris");
 311         ZoneId test = ZoneId.of("Europe/Madrid", map);
 312         assertEquals(test.getId(), "Europe/Madrid");
 313     }
 314 
 315     @Test
 316     public void test_of_string_Map_emptyMap() {
 317         Map<String, String> map = new HashMap<>();
 318         ZoneId test = ZoneId.of("Europe/Madrid", map);
 319         assertEquals(test.getId(), "Europe/Madrid");
 320     }
 321 
 322     @Test(expectedExceptions=DateTimeException.class)
 323     public void test_of_string_Map_badFormat() {
 324         Map<String, String> map = new HashMap<>();
 325         ZoneId.of("Not known", map);
 326     }
 327 
 328     @Test(expectedExceptions=ZoneRulesException.class)
 329     public void test_of_string_Map_unknown() {
 330         Map<String, String> map = new HashMap<>();
 331         ZoneId.of("Unknown", map);
 332     }
 333 
 334     //-----------------------------------------------------------------------
 335     // regular factory and .normalized()
 336     //-----------------------------------------------------------------------
 337     @DataProvider(name="offsetBasedValid")
 338     Object[][] data_offsetBasedValid() {
 339         return new Object[][] {
 340                 {"Z", "Z"},
 341                 {"+0", "Z"},
 342                 {"-0", "Z"},
 343                 {"+00", "Z"},
 344                 {"+0000", "Z"},
 345                 {"+00:00", "Z"},
 346                 {"+000000", "Z"},
 347                 {"+00:00:00", "Z"},
 348                 {"-00", "Z"},
 349                 {"-0000", "Z"},
 350                 {"-00:00", "Z"},
 351                 {"-000000", "Z"},
 352                 {"-00:00:00", "Z"},
 353                 {"+5", "+05:00"},
 354                 {"+01", "+01:00"},
 355                 {"+0100", "+01:00"},
 356                 {"+01:00", "+01:00"},
 357                 {"+010000", "+01:00"},
 358                 {"+01:00:00", "+01:00"},
 359                 {"+12", "+12:00"},
 360                 {"+1234", "+12:34"},
 361                 {"+12:34", "+12:34"},
 362                 {"+123456", "+12:34:56"},
 363                 {"+12:34:56", "+12:34:56"},
 364                 {"-02", "-02:00"},
 365                 {"-5", "-05:00"},
 366                 {"-0200", "-02:00"},
 367                 {"-02:00", "-02:00"},
 368                 {"-020000", "-02:00"},
 369                 {"-02:00:00", "-02:00"},
 370         };
 371     }
 372 
 373     @Test(dataProvider="offsetBasedValid")
 374     public void factory_of_String_offsetBasedValid_noPrefix(String input, String id) {
 375         ZoneId test = ZoneId.of(input);
 376         assertEquals(test.getId(), id);
 377         assertEquals(test, ZoneOffset.of(id));
 378         assertEquals(test.normalized(), ZoneOffset.of(id));
 379         assertEquals(test.getDisplayName(TextStyle.FULL, Locale.UK), id);
 380         assertEquals(test.getRules().isFixedOffset(), true);
 381         assertEquals(test.getRules().getOffset(Instant.EPOCH), ZoneOffset.of(id));
 382     }
 383 
 384     //-----------------------------------------------------------------------
 385     @DataProvider(name="offsetBasedValidPrefix")
 386     Object[][] data_offsetBasedValidPrefix() {
 387         return new Object[][] {
 388                 {"", "", "Z"},
 389                 {"+0", "", "Z"},
 390                 {"-0", "", "Z"},
 391                 {"+00", "", "Z"},
 392                 {"+0000", "", "Z"},
 393                 {"+00:00", "", "Z"},
 394                 {"+000000", "", "Z"},
 395                 {"+00:00:00", "", "Z"},
 396                 {"-00", "", "Z"},
 397                 {"-0000", "", "Z"},
 398                 {"-00:00", "", "Z"},
 399                 {"-000000", "", "Z"},
 400                 {"-00:00:00", "", "Z"},
 401                 {"+5", "+05:00", "+05:00"},
 402                 {"+01", "+01:00", "+01:00"},
 403                 {"+0100", "+01:00", "+01:00"},
 404                 {"+01:00", "+01:00", "+01:00"},
 405                 {"+010000", "+01:00", "+01:00"},
 406                 {"+01:00:00", "+01:00", "+01:00"},
 407                 {"+12", "+12:00", "+12:00"},
 408                 {"+1234", "+12:34", "+12:34"},
 409                 {"+12:34", "+12:34", "+12:34"},
 410                 {"+123456", "+12:34:56", "+12:34:56"},
 411                 {"+12:34:56", "+12:34:56", "+12:34:56"},
 412                 {"-02", "-02:00", "-02:00"},
 413                 {"-5", "-05:00", "-05:00"},
 414                 {"-0200", "-02:00", "-02:00"},
 415                 {"-02:00", "-02:00", "-02:00"},
 416                 {"-020000", "-02:00", "-02:00"},
 417                 {"-02:00:00", "-02:00", "-02:00"},
 418         };
 419     }
 420 
 421     @Test(dataProvider="offsetBasedValidPrefix")
 422     public void factory_of_String_offsetBasedValid_prefixUTC(String input, String id, String offsetId) {
 423         ZoneId test = ZoneId.of("UTC" + input);
 424         assertEquals(test.getId(), "UTC" + id);
 425         assertEquals(test.getRules(), ZoneOffset.of(offsetId).getRules());
 426         assertEquals(test.normalized(), ZoneOffset.of(offsetId));
 427         assertEquals(test.getDisplayName(TextStyle.FULL, Locale.UK), displayName("UTC" + id));
 428         assertEquals(test.getRules().isFixedOffset(), true);
 429         assertEquals(test.getRules().getOffset(Instant.EPOCH), ZoneOffset.of(offsetId));
 430     }
 431 
 432     @Test(dataProvider="offsetBasedValidPrefix")
 433     public void factory_of_String_offsetBasedValid_prefixGMT(String input, String id, String offsetId) {
 434         ZoneId test = ZoneId.of("GMT" + input);
 435         assertEquals(test.getId(), "GMT" + id);
 436         assertEquals(test.getRules(), ZoneOffset.of(offsetId).getRules());
 437         assertEquals(test.normalized(), ZoneOffset.of(offsetId));
 438         assertEquals(test.getDisplayName(TextStyle.FULL, Locale.UK), displayName("GMT" + id));
 439         assertEquals(test.getRules().isFixedOffset(), true);
 440         assertEquals(test.getRules().getOffset(Instant.EPOCH), ZoneOffset.of(offsetId));
 441     }
 442 
 443     @Test(dataProvider="offsetBasedValidPrefix")
 444     public void factory_of_String_offsetBasedValid_prefixUT(String input, String id, String offsetId) {
 445         ZoneId test = ZoneId.of("UT" + input);
 446         assertEquals(test.getId(), "UT" + id);
 447         assertEquals(test.getRules(), ZoneOffset.of(offsetId).getRules());
 448         assertEquals(test.normalized(), ZoneOffset.of(offsetId));
 449         assertEquals(test.getDisplayName(TextStyle.FULL, Locale.UK), displayName("UT" + id));
 450         assertEquals(test.getRules().isFixedOffset(), true);
 451         assertEquals(test.getRules().getOffset(Instant.EPOCH), ZoneOffset.of(offsetId));
 452     }
 453 
 454     private String displayName(String id) {
 455         if (id.equals("GMT")) {
 456             return "Greenwich Mean Time";
 457         }
 458         if (id.equals("GMT0")) {
 459             return "Greenwich Mean Time";
 460         }
 461         if (id.equals("UTC")) {
 462             return "Coordinated Universal Time";
 463         }
 464         return id;
 465     }
 466 
 467     //-----------------------------------------------------------------------
 468     @DataProvider(name="prefixValid")
 469     Object[][] data_prefixValid() {
 470         return new Object[][] {
 471                 {"GMT", "+01:00"},
 472                 {"UTC", "+01:00"},
 473                 {"UT", "+01:00"},
 474                 {"", "+01:00"},
 475         };
 476     }
 477 
 478     @Test(dataProvider="prefixValid")
 479     public void test_prefixOfOffset(String prefix, String offset) {
 480         ZoneOffset zoff = ZoneOffset.of(offset);
 481         ZoneId zoneId = ZoneId.ofOffset(prefix, zoff);
 482         assertEquals(zoneId.getId(), prefix + zoff.getId(), "in correct id for : " + prefix + ", zoff: " + zoff);
 483 
 484     }
 485 
 486     //-----------------------------------------------------------------------
 487     @DataProvider(name="prefixInvalid")
 488     Object[][] data_prefixInvalid() {
 489         return new Object[][] {
 490                 {"GM", "+01:00"},
 491                 {"U", "+01:00"},
 492                 {"UTC0", "+01:00"},
 493                 {"A", "+01:00"},
 494         };
 495     }
 496 
 497     @Test(dataProvider="prefixInvalid", expectedExceptions=java.lang.IllegalArgumentException.class)
 498     public void test_invalidPrefixOfOffset(String prefix, String offset) {
 499         ZoneOffset zoff = ZoneOffset.of(offset);
 500         ZoneId zoneId = ZoneId.ofOffset(prefix, zoff);
 501         fail("should have thrown an exception for prefix: " + prefix);
 502     }
 503 
 504     @Test(expectedExceptions=java.lang.NullPointerException.class)
 505     public void test_nullPrefixOfOffset() {
 506         ZoneId.ofOffset(null, ZoneOffset.ofTotalSeconds(1));
 507     }
 508 
 509     @Test(expectedExceptions=java.lang.NullPointerException.class)
 510     public void test_nullOffsetOfOffset() {
 511         ZoneId.ofOffset("GMT", null);
 512     }
 513 
 514     //-----------------------------------------------------------------------
 515     @DataProvider(name="offsetBasedValidOther")
 516     Object[][] data_offsetBasedValidOther() {
 517         return new Object[][] {
 518                 {"GMT", "Z"},
 519                 {"GMT0", "Z"},
 520                 {"UCT", "Z"},
 521                 {"Greenwich", "Z"},
 522                 {"Universal", "Z"},
 523                 {"Zulu", "Z"},
 524                 {"Etc/GMT", "Z"},
 525                 {"Etc/GMT+0", "Z"},
 526                 {"Etc/GMT+1", "-01:00"},
 527                 {"Etc/GMT-1", "+01:00"},
 528                 {"Etc/GMT+9", "-09:00"},
 529                 {"Etc/GMT-9", "+09:00"},
 530                 {"Etc/GMT0", "Z"},
 531                 {"Etc/UCT", "Z"},
 532                 {"Etc/UTC", "Z"},
 533                 {"Etc/Greenwich", "Z"},
 534                 {"Etc/Universal", "Z"},
 535                 {"Etc/Zulu", "Z"},
 536         };
 537     }
 538 
 539     @Test(dataProvider="offsetBasedValidOther")
 540     public void factory_of_String_offsetBasedValidOther(String input, String offsetId) {
 541         ZoneId test = ZoneId.of(input);
 542         assertEquals(test.getId(), input);
 543         assertEquals(test.getRules(), ZoneOffset.of(offsetId).getRules());
 544         assertEquals(test.normalized(), ZoneOffset.of(offsetId));
 545     }
 546 
 547     //-----------------------------------------------------------------------
 548     @DataProvider(name="offsetBasedInvalid")
 549     Object[][] data_offsetBasedInvalid() {
 550         return new Object[][] {
 551                 {"A"}, {"B"}, {"C"}, {"D"}, {"E"}, {"F"}, {"G"}, {"H"}, {"I"}, {"J"}, {"K"}, {"L"}, {"M"},
 552                 {"N"}, {"O"}, {"P"}, {"Q"}, {"R"}, {"S"}, {"T"}, {"U"}, {"V"}, {"W"}, {"X"}, {"Y"}, {"Z"},
 553                 {"+0:00"}, {"+00:0"}, {"+0:0"},
 554                 {"+000"}, {"+00000"},
 555                 {"+0:00:00"}, {"+00:0:00"}, {"+00:00:0"}, {"+0:0:0"}, {"+0:0:00"}, {"+00:0:0"}, {"+0:00:0"},
 556                 {"+01_00"}, {"+01;00"}, {"+01@00"}, {"+01:AA"},
 557                 {"+19"}, {"+19:00"}, {"+18:01"}, {"+18:00:01"}, {"+1801"}, {"+180001"},
 558                 {"-0:00"}, {"-00:0"}, {"-0:0"},
 559                 {"-000"}, {"-00000"},
 560                 {"-0:00:00"}, {"-00:0:00"}, {"-00:00:0"}, {"-0:0:0"}, {"-0:0:00"}, {"-00:0:0"}, {"-0:00:0"},
 561                 {"-19"}, {"-19:00"}, {"-18:01"}, {"-18:00:01"}, {"-1801"}, {"-180001"},
 562                 {"-01_00"}, {"-01;00"}, {"-01@00"}, {"-01:AA"},
 563                 {"@01:00"},
 564                 {"0"},
 565                 {"UT0"},
 566                 {"UTZ"},
 567                 {"UTC0"},
 568                 {"UTCZ"},
 569                 {"GMTZ"},  // GMT0 is valid in ZoneRulesProvider
 570         };
 571     }
 572 
 573     @Test(dataProvider="offsetBasedInvalid", expectedExceptions=DateTimeException.class)
 574     public void factory_of_String_offsetBasedInvalid_noPrefix(String id) {
 575         if (id.equals("Z")) {
 576             throw new DateTimeException("Fake exception: Z alone is valid, not invalid");
 577         }
 578         ZoneId.of(id);
 579     }
 580 
 581     @Test(dataProvider="offsetBasedInvalid", expectedExceptions=DateTimeException.class)
 582     public void factory_of_String_offsetBasedInvalid_prefixUTC(String id) {
 583         ZoneId.of("UTC" + id);
 584     }
 585 
 586     @Test(dataProvider="offsetBasedInvalid", expectedExceptions=DateTimeException.class)
 587     public void factory_of_String_offsetBasedInvalid_prefixGMT(String id) {
 588         if (id.equals("0")) {
 589             throw new DateTimeException("Fake exception: GMT0 is valid, not invalid");
 590         }
 591         ZoneId.of("GMT" + id);
 592     }
 593 
 594     @Test(dataProvider="offsetBasedInvalid", expectedExceptions=DateTimeException.class)
 595     public void factory_of_String_offsetBasedInvalid_prefixUT(String id) {
 596         if (id.equals("C")) {
 597             throw new DateTimeException("Fake exception: UT + C = UTC, thus it is valid, not invalid");
 598         }
 599         ZoneId.of("UT" + id);
 600     }
 601 
 602     //-----------------------------------------------------------------------
 603     @DataProvider(name="regionBasedInvalid")
 604     Object[][] data_regionBasedInvalid() {
 605         // \u00ef is a random unicode character
 606         return new Object[][] {
 607                 {""}, {":"}, {"#"},
 608                 {"\u00ef"}, {"`"}, {"!"}, {"\""}, {"\u00ef"}, {"$"}, {"^"}, {"&"}, {"*"}, {"("}, {")"}, {"="},
 609                 {"\\"}, {"|"}, {","}, {"<"}, {">"}, {"?"}, {";"}, {"'"}, {"["}, {"]"}, {"{"}, {"}"},
 610                 {"\u00ef:A"}, {"`:A"}, {"!:A"}, {"\":A"}, {"\u00ef:A"}, {"$:A"}, {"^:A"}, {"&:A"}, {"*:A"}, {"(:A"}, {"):A"}, {"=:A"}, {"+:A"},
 611                 {"\\:A"}, {"|:A"}, {",:A"}, {"<:A"}, {">:A"}, {"?:A"}, {";:A"}, {"::A"}, {"':A"}, {"@:A"}, {"~:A"}, {"[:A"}, {"]:A"}, {"{:A"}, {"}:A"},
 612                 {"A:B#\u00ef"}, {"A:B#`"}, {"A:B#!"}, {"A:B#\""}, {"A:B#\u00ef"}, {"A:B#$"}, {"A:B#^"}, {"A:B#&"}, {"A:B#*"},
 613                 {"A:B#("}, {"A:B#)"}, {"A:B#="}, {"A:B#+"},
 614                 {"A:B#\\"}, {"A:B#|"}, {"A:B#,"}, {"A:B#<"}, {"A:B#>"}, {"A:B#?"}, {"A:B#;"}, {"A:B#:"},
 615                 {"A:B#'"}, {"A:B#@"}, {"A:B#~"}, {"A:B#["}, {"A:B#]"}, {"A:B#{"}, {"A:B#}"},
 616         };
 617     }
 618 
 619     @Test(dataProvider="regionBasedInvalid", expectedExceptions=DateTimeException.class)
 620     public void factory_of_String_regionBasedInvalid(String id) {
 621         ZoneId.of(id);
 622     }
 623 
 624     //-----------------------------------------------------------------------
 625     @Test
 626     public void factory_of_String_region_EuropeLondon() {
 627         ZoneId test = ZoneId.of("Europe/London");
 628         assertEquals(test.getId(), "Europe/London");
 629         assertEquals(test.getRules().isFixedOffset(), false);
 630         assertEquals(test.normalized(), test);
 631     }
 632 
 633     //-----------------------------------------------------------------------
 634     @Test(expectedExceptions=NullPointerException.class)
 635     public void factory_of_String_null() {
 636         ZoneId.of(null);
 637     }
 638 
 639     @Test(expectedExceptions=DateTimeException.class)
 640     public void factory_of_String_badFormat() {
 641         ZoneId.of("Unknown rule");
 642     }
 643 
 644     @Test(expectedExceptions=ZoneRulesException.class)
 645     public void factory_of_String_unknown() {
 646         ZoneId.of("Unknown");
 647     }
 648 
 649     //-----------------------------------------------------------------------
 650     // from(TemporalAccessor)
 651     //-----------------------------------------------------------------------
 652     @Test
 653     public void factory_from_TemporalAccessor_zoneId() {
 654         TemporalAccessor mock = new TemporalAccessor() {
 655             @Override
 656             public boolean isSupported(TemporalField field) {
 657                 return false;
 658             }
 659             @Override
 660             public long getLong(TemporalField field) {
 661                 throw new DateTimeException("Mock");
 662             }
 663             @SuppressWarnings("unchecked")
 664             @Override
 665             public <R> R query(TemporalQuery<R> query) {
 666                 if (query == TemporalQuery.zoneId()) {
 667                     return (R) ZoneId.of("Europe/Paris");
 668                 }
 669                 return TemporalAccessor.super.query(query);
 670             }
 671         };
 672         assertEquals(ZoneId.from(mock),  ZoneId.of("Europe/Paris"));
 673     }
 674 
 675     @Test
 676     public void factory_from_TemporalAccessor_offset() {
 677         ZoneOffset offset = ZoneOffset.ofHours(1);
 678         assertEquals(ZoneId.from(offset), offset);
 679     }
 680 
 681     @Test(expectedExceptions=DateTimeException.class)
 682     public void factory_from_TemporalAccessor_invalid_noDerive() {
 683         ZoneId.from(LocalTime.of(12, 30));
 684     }
 685 
 686     @Test(expectedExceptions=NullPointerException.class)
 687     public void factory_from_TemporalAccessor_null() {
 688         ZoneId.from(null);
 689     }
 690 
 691     //-----------------------------------------------------------------------
 692     // equals() / hashCode()
 693     //-----------------------------------------------------------------------
 694     @Test
 695     public void test_equals() {
 696         ZoneId test1 = ZoneId.of("Europe/London");
 697         ZoneId test2 = ZoneId.of("Europe/Paris");
 698         ZoneId test2b = ZoneId.of("Europe/Paris");
 699         assertEquals(test1.equals(test2), false);
 700         assertEquals(test2.equals(test1), false);
 701 
 702         assertEquals(test1.equals(test1), true);
 703         assertEquals(test2.equals(test2), true);
 704         assertEquals(test2.equals(test2b), true);
 705 
 706         assertEquals(test1.hashCode() == test1.hashCode(), true);
 707         assertEquals(test2.hashCode() == test2.hashCode(), true);
 708         assertEquals(test2.hashCode() == test2b.hashCode(), true);
 709     }
 710 
 711     @Test
 712     public void test_equals_null() {
 713         assertEquals(ZoneId.of("Europe/London").equals(null), false);
 714     }
 715 
 716     @Test
 717     public void test_equals_notEqualWrongType() {
 718         assertEquals(ZoneId.of("Europe/London").equals("Europe/London"), false);
 719     }
 720 
 721     //-----------------------------------------------------------------------
 722     // toString()
 723     //-----------------------------------------------------------------------
 724     @DataProvider(name="toString")
 725     Object[][] data_toString() {
 726         return new Object[][] {
 727                 {"Europe/London", "Europe/London"},
 728                 {"Europe/Paris", "Europe/Paris"},
 729                 {"Europe/Berlin", "Europe/Berlin"},
 730                 {"Z", "Z"},
 731                 {"+01:00", "+01:00"},
 732                 {"UTC", "UTC"},
 733                 {"UTC+01:00", "UTC+01:00"},
 734         };
 735     }
 736 
 737     @Test(dataProvider="toString")
 738     public void test_toString(String id, String expected) {
 739         ZoneId test = ZoneId.of(id);
 740         assertEquals(test.toString(), expected);
 741     }
 742 
 743 }