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