1 /*
   2  * Copyright (c) 2002, 2016, 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  * @test
  26  * @bug 6405639 8008577 8151876
  27  * @summary Validate timezone display names in
  28  *          src/java.base/share/classes/sun/util/resources/TimeZoneNames.java.
  29  * @modules java.base/sun.util.resources
  30  * @compile -XDignore.symbol.file CheckDisplayNames.java
  31  * @run main/othervm -Djava.locale.providers=COMPAT,SPI CheckDisplayNames
  32  */
  33 
  34 import java.util.*;
  35 import sun.util.resources.TimeZoneNames;
  36 
  37 /**
  38  * CheckDisplayNames checks all available time zones in the Java run
  39  * time environment and sees if those have their display names besides doing
  40  * some other test cases. It outputs time zones that don't have display names
  41  * if -source option is specified.
  42  * <blockquote>
  43  * <pre>
  44  *    Usage: java CheckDisplayNames [-source]
  45  *              -source ... produces source code for editing TimeZoneNames.java.
  46  * </pre>
  47  * </blockquote>
  48  */
  49 public class CheckDisplayNames {
  50 
  51     private static boolean err = false;
  52     private static boolean src = false;
  53 
  54     private static Locale[] locales = Locale.getAvailableLocales();
  55     private static String[] zones = TimeZone.getAvailableIDs();
  56     private static List<String> newGMTzones = new ArrayList<>();
  57     
  58     public static void setNewGMTzones() {
  59         for (String zone : zones) {
  60             String shortName = TimeZone.getTimeZone(zone).getDisplayName(false, TimeZone.SHORT);
  61             String longName = TimeZone.getTimeZone(zone).getDisplayName(false, TimeZone.LONG);
  62             if (!(zone.contains("GMT+") || zone.contains("GMT-")) && shortName.equals(longName)) {
  63                 newGMTzones.add(TimeZone.getTimeZone(zone).getDisplayName());
  64             }
  65         }
  66     }
  67 
  68     private static String[] zones_118 = {
  69         "ACT",  "Australia/Darwin",
  70         "AET",  "Australia/Sydney",
  71         "AGT",  "America/Buenos_Aires",
  72         "ART",  "Africa/Cairo",
  73         "AST",  "America/Anchorage",
  74         "BET",  "America/Sao_Paulo",
  75         "BST",  "Asia/Dacca",
  76         "CAT",  "Africa/Harare",
  77         "CNT",  "America/St_Johns",
  78         "CST",  "America/Chicago",
  79         "CTT",  "Asia/Shanghai",
  80         "EAT",  "Africa/Addis_Ababa",
  81         "ECT",  "Europe/Paris",
  82 //      "EET",  "Africa/Istanbul",
  83         "EST",  "America/New_York",
  84         "HST",  "Pacific/Honolulu",
  85         "IET",  "America/Indiana/Indianapolis",
  86 //      Comment out for this test case fails as the result of L10N for hi_IN.
  87 //      "IST",  "Asia/Calcutta",
  88         "JST",  "Asia/Tokyo",
  89 //      "MET",  "Asia/Tehran",
  90         "MIT",  "Pacific/Apia",
  91         "MST",  "America/Denver",
  92         "NET",  "Asia/Yerevan",
  93         "NST",  "Pacific/Auckland",
  94         "PLT",  "Asia/Karachi",
  95         "PNT",  "America/Phoenix",
  96         "PRT",  "America/Puerto_Rico",
  97         "PST",  "America/Los_Angeles",
  98         "SST",  "Pacific/Guadalcanal",
  99         "VST",  "Asia/Saigon",
 100     };
 101 
 102 
 103     public static void main(String[] argv) {
 104         Locale reservedLocale = Locale.getDefault();
 105         try {
 106             if (argv.length == 1 && "-source".equals(argv[0])) {
 107                 src = true;
 108             }
 109 
 110             setNewGMTzones();
 111             testDisplayNames();
 112             testRAWoffsetAndDisplayNames();
 113             test118DisplayNames();
 114 
 115             if (err) {
 116                 throw new RuntimeException(
 117                     "TimeZone display name validation failed.");
 118             } else {
 119                 System.out.println(
 120                     "\nAll test passed.\nTotal number of valid TimeZone id is "
 121                     + zones.length);
 122             }
 123         } finally {
 124             // restore the reserved locale
 125             Locale.setDefault(reservedLocale);
 126         }
 127 
 128     }
 129 
 130     /*
 131      * Checks if each timezone ID has display names. If it doesn't and
 132      * "-source" option was specified, source code is generated.
 133      */
 134     private static void testDisplayNames() {
 135         System.out.println("Checking if each entry in TimeZoneNames is a valid TimeZone ID");
 136 
 137         Locale.setDefault(Locale.US);
 138         Enumeration data = new TimeZoneNames().getKeys();
 139 
 140         while (data.hasMoreElements()) {
 141             String name = (String)data.nextElement();
 142             String id = TimeZone.getTimeZone(name).getID();
 143             if (!name.equals(id)) {
 144                 System.err.println("\t" + name + " doesn't seem to be a valid TimeZone ID.");
 145                 err = true;
 146             }
 147         }
 148 
 149         System.out.println("Checking if each TimeZone ID has display names.");
 150 
 151         for (int i = 0; i < zones.length; i++) {
 152             String id = zones[i];
 153 
 154             if (id != null) {
 155                 if (id.startsWith("Etc/GMT")) {
 156                     continue;
 157                 }
 158                 if (id.indexOf("Riyadh8") != -1) {
 159                     continue;
 160                 }
 161                 if (id.equals("GMT0")) {
 162                     continue;
 163                 }
 164             }
 165 
 166             TimeZone tz = TimeZone.getTimeZone(id);
 167             String name = tz.getDisplayName();
 168 
 169             if ((name == null || name.startsWith("GMT+") || name.startsWith("GMT-")) && !newGMTzones.contains(name)) {
 170                 if (src) {
 171                     System.out.println("\t    {\"" + tz.getID() + "\", " +
 172                                        "new String[] {\"Standard Time Name\", \"ST\",\n" +
 173                                        "\t\t\t\t\t\t\"Daylight Time Name\", \"DT\"}},");
 174                 } else {
 175                     System.err.println("\t" + tz.getID() + " doesn't seem to have display names");
 176                     err = true;
 177                 }
 178             }
 179         }
 180     }
 181 
 182     /*
 183      * Compares
 184      *   - raw DST offset
 185      *   - short display names in non-DST
 186      *   - short display names in DST
 187      *   - long display names in DST
 188      * of two timezones whose long display names in non-DST are same.
 189      * If one of these are different, there may be a bug.
 190      */
 191     private static void testRAWoffsetAndDisplayNames() {
 192         System.out.println("Checking if each entry in TimeZoneNames is a valid TimeZone ID");
 193 
 194         HashMap<String, TimeZone> map = new HashMap<String, TimeZone>();
 195 
 196         for (int i = 0; i < locales.length; i++) {
 197             map.clear();
 198 
 199             for (int j = 0; j < zones.length; j++) {
 200                 TimeZone tz1 = TimeZone.getTimeZone(zones[j]);
 201                 String name = tz1.getDisplayName(false, TimeZone.LONG, locales[i]);
 202 
 203                 if (map.containsKey(name)) {
 204                     TimeZone tz2 = map.get(name);
 205 
 206                     int offset1 = tz1.getRawOffset();
 207                     int offset2 = tz2.getRawOffset();
 208                     if (offset1 != offset2) {
 209                         System.err.println("Two timezones which have the same long display name \"" +
 210                             name + "\" in non-DST have different DST offsets in " +
 211                             locales[i] + " locale.\n\tTimezone 1=" +
 212                             tz1.getID() + "(" + offset1 + ")\n\tTimezone 2=" +
 213                             tz2.getID() + "(" + offset2 + ")");
 214                     }
 215 
 216                     String name1 = tz1.getDisplayName(false, TimeZone.SHORT, locales[i]);
 217                     String name2 = tz2.getDisplayName(false, TimeZone.SHORT, locales[i]);
 218                     if (!(name1.equals("GMT") && name2.equals("GMT")) &&
 219                         !(name1.equals("CET") && name2.equals("MET")) &&
 220                         !(name1.equals("MET") && name2.equals("CET"))) {
 221                         if (!name1.equals(name2)) {
 222                             System.err.println("Two timezones which have the same short display name \"" +
 223                                 name +
 224                                 "\" in non-DST have different short display names in non-DST in " +
 225                                 locales[i] + " locale.\n\tTimezone 1=" +
 226                                 tz1.getID() + "(" + name1 + ")\n\tTimezone 2=" +
 227                                 tz2.getID() + "(" + name2 + ")");
 228                         }
 229 
 230                         name1 = tz1.getDisplayName(true, TimeZone.SHORT, locales[i]);
 231                         name2 = tz2.getDisplayName(true, TimeZone.SHORT, locales[i]);
 232                         if (!name1.equals(name2)) {
 233                             System.err.println("Two timezones which have the same short display name \"" +
 234                             name +
 235                             "\" in non-DST have different short display names in DST in " +
 236                             locales[i] + " locale.\n\tTimezone 1=" +
 237                             tz1.getID() + "(" + name1 + ")\n\tTimezone 2=" +
 238                             tz2.getID() + "(" + name2 + ")");
 239                         }
 240 
 241                         name1 = tz1.getDisplayName(true, TimeZone.LONG, locales[i]);
 242                         name2 = tz2.getDisplayName(true, TimeZone.LONG, locales[i]);
 243                         if (!name1.equals(name2)) {
 244                             System.err.println("Two timezones which have the same long display name \"" +
 245                             name +
 246                             "\" in non-DST have different long display names in DST in " +
 247                             locales[i] + " locale.\n\tTimezone 1=" +
 248                             tz1.getID() + "(" + name1 + ")\n\tTimezone 2=" +
 249                             tz2.getID() + "(" + name2 + ")");
 250                         }
 251                     }
 252                 } else {
 253                     map.put(name, tz1);
 254                 }
 255             }
 256         }
 257     }
 258 
 259     /*
 260      * Compares three-letter timezones' display names with corresponding
 261      * "popular" timezones.
 262      */
 263     private static void test118DisplayNames() {
 264         System.out.println("Checking compatibility of Java 1.1.X's three-letter timezones");
 265 
 266         for (int i = 0; i < zones_118.length; i+=2) {
 267             String id_118 = zones_118[i];
 268             String id_later = zones_118[i+1];
 269             String zone_118, zone_later, localename;
 270             TimeZone tz_118 = TimeZone.getTimeZone(id_118);
 271             TimeZone tz_later = TimeZone.getTimeZone(id_later);
 272 
 273             for (int j = 0; j < locales.length; j++) {
 274                 localename = locales[j].toString();
 275                 zone_118 = tz_118.getDisplayName(false, TimeZone.SHORT, locales[j]);
 276                 zone_later = tz_later.getDisplayName(false, TimeZone.SHORT, locales[j]);
 277                 check(id_118, id_later, zone_118, zone_later, "short", "non-DST", localename);
 278 
 279                 zone_118 = tz_118.getDisplayName(true, TimeZone.SHORT, locales[j]);
 280                 zone_later = tz_later.getDisplayName(true, TimeZone.SHORT, locales[j]);
 281                 check(id_118, id_later, zone_118, zone_later, "short", "DST", localename);
 282 
 283                 zone_118 = tz_118.getDisplayName(false, TimeZone.LONG, locales[j]);
 284                 zone_later = tz_later.getDisplayName(false, TimeZone.LONG, locales[j]);
 285                 check(id_118, id_later, zone_118, zone_later, "long", "non-DST", localename);
 286 
 287                 zone_118 = tz_118.getDisplayName(true, TimeZone.LONG, locales[j]);
 288                 zone_later = tz_later.getDisplayName(true, TimeZone.LONG, locales[j]);
 289                 check(id_118, id_later, zone_118, zone_later, "long", "DST", localename);
 290             }
 291         }
 292     }
 293 
 294     private static void check(String zoneID_118, String zoneID_later,
 295                               String zonename_118, String zonename_later,
 296                               String format, String dst, String loc) {
 297         if (!zonename_118.equals(zonename_later)) {
 298             System.err.println("JDK 118 TimeZone \"" + zoneID_118 +
 299                 "\" has a different " + format +
 300                 " display name from its equivalent timezone \"" +
 301                 zoneID_later + "\" in " + dst + " in " + loc + " locale.");
 302             System.err.println("    Got: " + zonename_118 + ", Expected: " +
 303                 zonename_later);
 304             err = true;
 305         }
 306     }
 307 
 308 }