12d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// © 2016 and later: Unicode, Inc. and others.
22d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html#License
37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ********************************************************************************
5e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer * Copyright (C) 2007-2016, Google, International Business Machines Corporation
6e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer * and others. All Rights Reserved.
77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ********************************************************************************
87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.dev.test.format;
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12e7a7efded856b8959849879938d1cb2e8ccab318Markus Schererimport java.text.FieldPosition;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.ParseException;
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.ParsePosition;
15f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubertimport java.util.ArrayList;
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Arrays;
172d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubertimport java.util.Collections;
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Date;
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.EnumSet;
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.List;
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Locale;
222d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubertimport java.util.Random;
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Set;
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.TreeSet;
25f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubertimport java.util.concurrent.atomic.AtomicInteger;
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.regex.Pattern;
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
282d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubertimport org.junit.Test;
292d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
302d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubertimport com.ibm.icu.dev.test.TestFmwk;
31f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubertimport com.ibm.icu.impl.TZDBTimeZoneNames;
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.ZoneMeta;
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.lang.UCharacter;
34e7a7efded856b8959849879938d1cb2e8ccab318Markus Schererimport com.ibm.icu.text.DateFormat;
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.SimpleDateFormat;
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.TimeZoneFormat;
37e7a7efded856b8959849879938d1cb2e8ccab318Markus Schererimport com.ibm.icu.text.TimeZoneFormat.GMTOffsetPatternType;
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.TimeZoneFormat.ParseOption;
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.TimeZoneFormat.Style;
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.TimeZoneFormat.TimeType;
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.TimeZoneNames;
422d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubertimport com.ibm.icu.text.TimeZoneNames.Factory;
43f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubertimport com.ibm.icu.text.TimeZoneNames.NameType;
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.BasicTimeZone;
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.Calendar;
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.Output;
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.SimpleTimeZone;
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.TimeZone;
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.TimeZone.SystemTimeZoneType;
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.TimeZoneTransition;
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ULocale;
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic class TimeZoneFormatTest extends com.ibm.icu.dev.test.TestFmwk {
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static boolean JDKTZ = (TimeZone.getDefaultTimeZoneType() == TimeZone.TIMEZONE_JDK);
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final Pattern EXCL_TZ_PATTERN = Pattern.compile(".*/Riyadh8[7-9]");
577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final String[] PATTERNS = {
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "z",
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "zzzz",
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "Z",        // equivalent to "xxxx"
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "ZZZZ",     // equivalent to "OOOO"
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "v",
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "vvvv",
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "O",
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "OOOO",
677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "X",
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "XX",
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "XXX",
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "XXXX",
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "XXXXX",
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "x",
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "xx",
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "xxx",
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "xxxx",
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "xxxxx",
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "V",
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "VV",
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "VVV",
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "VVVV"
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    };
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    boolean REALLY_VERBOSE_LOG = false;
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Test case for checking if a TimeZone is properly set in the result calendar
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and if the result TimeZone has the expected behavior.
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
882d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Test
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void TestTimeZoneRoundTrip() {
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean TEST_ALL = getBooleanProperty("TimeZoneRoundTripAll", false);
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        TimeZone unknownZone = new SimpleTimeZone(-31415, "Etc/Unknown");
937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int badDstOffset = -1234;
947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int badZoneOffset = -2345;
957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int[][] testDateData = {
977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {2007, 1, 15},
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {2007, 6, 15},
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {1990, 1, 15},
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {1990, 6, 15},
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {1960, 1, 15},
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {1960, 6, 15},
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        };
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        cal.clear();
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Set up rule equivalency test range
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long low, high;
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        cal.set(1900, 0, 1);
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        low = cal.getTimeInMillis();
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        cal.set(2040, 0, 1);
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        high = cal.getTimeInMillis();
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Set up test dates
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Date[] DATES = new Date[testDateData.length];
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        cal.clear();
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < DATES.length; i++) {
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            cal.set(testDateData[i][0], testDateData[i][1], testDateData[i][2]);
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            DATES[i] = cal.getTime();
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Set up test locales
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ULocale[] LOCALES = null;
1252d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        if (TEST_ALL || TestFmwk.getExhaustiveness() > 5) {
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            LOCALES = ULocale.getAvailableLocales();
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            LOCALES = new ULocale[] {new ULocale("en"), new ULocale("en_CA"), new ULocale("fr"), new ULocale("zh_Hant")};
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String[] tzids;
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (JDKTZ) {
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            tzids = java.util.TimeZone.getAvailableIDs();
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            tzids = TimeZone.getAvailableIDs();
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int[] inOffsets = new int[2];
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int[] outOffsets = new int[2];
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Run the roundtrip test
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int locidx = 0; locidx < LOCALES.length; locidx++) {
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            logln("Locale: " + LOCALES[locidx].toString());
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String localGMTString = TimeZoneFormat.getInstance(LOCALES[locidx]).formatOffsetLocalizedGMT(0);
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int patidx = 0; patidx < PATTERNS.length; patidx++) {
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                logln("    pattern: " + PATTERNS[patidx]);
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                SimpleDateFormat sdf = new SimpleDateFormat(PATTERNS[patidx], LOCALES[locidx]);
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int tzidx = 0; tzidx < tzids.length; tzidx++) {
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (EXCL_TZ_PATTERN.matcher(tzids[tzidx]).matches()) {
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        continue;
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    TimeZone tz = TimeZone.getTimeZone(tzids[tzidx]);
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for (int datidx = 0; datidx < DATES.length; datidx++) {
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // Format
1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        sdf.setTimeZone(tz);
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        String tzstr = sdf.format(DATES[datidx]);
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // Before parse, set unknown zone to SimpleDateFormat instance
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // just for making sure that it does not depends on the time zone
1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // originally set.
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        sdf.setTimeZone(unknownZone);
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // Parse
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        ParsePosition pos = new ParsePosition(0);
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        Calendar outcal = Calendar.getInstance(unknownZone);
1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        outcal.set(Calendar.DST_OFFSET, badDstOffset);
1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        outcal.set(Calendar.ZONE_OFFSET, badZoneOffset);
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        sdf.parse(tzstr, outcal, pos);
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // Check the result
1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        TimeZone outtz = outcal.getTimeZone();
1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        tz.getOffset(DATES[datidx].getTime(), false, inOffsets);
1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        outtz.getOffset(DATES[datidx].getTime(), false, outOffsets);
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (PATTERNS[patidx].equals("V")) {
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // Short zone ID - should support roundtrip for canonical CLDR IDs
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            String canonicalID = TimeZone.getCanonicalID(tzids[tzidx]);
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (!outtz.getID().equals(canonicalID)) {
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                if (outtz.getID().equals("Etc/Unknown")) {
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    // Note that some zones like Asia/Riyadh87 does not have
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    // short zone ID and "unk" is used as the fallback
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    if (REALLY_VERBOSE_LOG) {
1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        logln("Canonical round trip failed (probably as expected); tz=" + tzids[tzidx]
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx]
1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr
1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            + ", outtz=" + outtz.getID());
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    }
1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                } else {
1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    errln("Canonical round trip failed; tz=" + tzids[tzidx]
1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx]
1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr
1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        + ", outtz=" + outtz.getID());
1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                }
1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (PATTERNS[patidx].equals("VV")) {
2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // Zone ID - full roundtrip support
2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (!outtz.getID().equals(tzids[tzidx])) {
2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                errln("Zone ID round trip failed; tz=" + tzids[tzidx]
2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx]
2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr
2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        + ", outtz=" + outtz.getID());
2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (PATTERNS[patidx].equals("VVV") || PATTERNS[patidx].equals("VVVV")) {
2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // Location: time zone rule must be preserved except
2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // zones not actually associated with a specific location.
2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            String canonicalID = TimeZone.getCanonicalID(tzids[tzidx]);
2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (canonicalID != null && !outtz.getID().equals(canonicalID)) {
2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                // Canonical ID did not match - check the rules
2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                boolean bFailure = false;
2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                if ((tz instanceof BasicTimeZone) && (outtz instanceof BasicTimeZone)) {
2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    boolean hasNoLocation = TimeZone.getRegion(tzids[tzidx]).equals("001");
2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    bFailure = !hasNoLocation
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                && !((BasicTimeZone)outtz).hasEquivalentTransitions(tz, low, high);
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                }
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                if (bFailure) {
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    errln("Canonical round trip failed; tz=" + tzids[tzidx]
2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx]
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            + ", outtz=" + outtz.getID());
2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                } else if (REALLY_VERBOSE_LOG) {
2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    logln("Canonical round trip failed (as expected); tz=" + tzids[tzidx]
2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx]
2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr
2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            + ", outtz=" + outtz.getID());
2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                }
2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else {
2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            boolean isOffsetFormat = (PATTERNS[patidx].charAt(0) == 'Z'
2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    || PATTERNS[patidx].charAt(0) == 'O'
2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    || PATTERNS[patidx].charAt(0) == 'X'
2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    || PATTERNS[patidx].charAt(0) == 'x');
2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            boolean minutesOffset = false;
2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (PATTERNS[patidx].charAt(0) == 'X' || PATTERNS[patidx].charAt(0) == 'x') {
2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                minutesOffset = PATTERNS[patidx].length() <= 3;
2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (!isOffsetFormat) {
2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                // Check if localized GMT format is used as a fallback of name styles
2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                int numDigits = 0;
2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                for (int n = 0; n < tzstr.length(); n++) {
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    if (UCharacter.isDigit(tzstr.charAt(n))) {
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        numDigits++;
2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    }
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                }
2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                isOffsetFormat = (numDigits > 0);
2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (isOffsetFormat || tzstr.equals(localGMTString)) {
2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                // Localized GMT or ISO: total offset (raw + dst) must be preserved.
2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                int inOffset = inOffsets[0] + inOffsets[1];
2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                int outOffset = outOffsets[0] + outOffsets[1];
2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                int diff = outOffset - inOffset;
2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                if (minutesOffset) {
2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    diff = (diff / 60000) * 60000;
2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                }
2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                if (diff != 0) {
2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    errln("Offset round trip failed; tz=" + tzids[tzidx]
2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx]
2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr
2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        + ", inOffset=" + inOffset + ", outOffset=" + outOffset);
2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                }
2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            } else {
2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                // Specific or generic: raw offset must be preserved.
2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                if (inOffsets[0] != outOffsets[0]) {
2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    if (JDKTZ && tzids[tzidx].startsWith("SystemV/")) {
2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        // JDK uses rule SystemV for these zones while
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        // ICU handles these zones as aliases of existing time zones
2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        if (REALLY_VERBOSE_LOG) {
2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            logln("Raw offset round trip failed; tz=" + tzids[tzidx]
2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx]
2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr
2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                + ", inRawOffset=" + inOffsets[0] + ", outRawOffset=" + outOffsets[0]);
2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        }
2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    } else {
2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        errln("Raw offset round trip failed; tz=" + tzids[tzidx]
2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx]
2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr
2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            + ", inRawOffset=" + inOffsets[0] + ", outRawOffset=" + outOffsets[0]);
2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    }
2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                }
2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Test case of round trip time and text.  This test case detects every canonical TimeZone's
2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * rule transition since 1900 until 2020, then check if time around each transition can
2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * round trip as expected.
3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3012d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Test
3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void TestTimeRoundTrip() {
3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean TEST_ALL = getBooleanProperty("TimeZoneRoundTripAll", false);
3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int startYear, endYear;
3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3082d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        if (TEST_ALL || TestFmwk.getExhaustiveness() > 5) {
3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            startYear = 1900;
3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            startYear = 1990;
3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        endYear = cal.get(Calendar.YEAR) + 3;
3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        cal.set(startYear, Calendar.JANUARY, 1);
3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final long START_TIME = cal.getTimeInMillis();
3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        cal.set(endYear, Calendar.JANUARY, 1);
3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final long END_TIME = cal.getTimeInMillis();
3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // These patterns are ambiguous at DST->STD local time overlap
3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        List<String> AMBIGUOUS_DST_DECESSION = Arrays.asList("v", "vvvv", "V", "VV", "VVV", "VVVV");
3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // These patterns are ambiguous at STD->STD/DST->DST local time overlap
3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        List<String> AMBIGUOUS_NEGATIVE_SHIFT = Arrays.asList("z", "zzzz", "v", "vvvv", "V", "VV", "VVV", "VVVV");
3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // These patterns only support integer minutes offset
3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        List<String> MINUTES_OFFSET = Arrays.asList("X", "XX", "XXX", "x", "xx", "xxx");
3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Regex pattern used for filtering zone IDs without exemplar location
3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final Pattern LOC_EXCLUSION_PATTERN = Pattern.compile("Etc/.*|SystemV/.*|.*/Riyadh8[7-9]");
3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final String BASEPATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS";
3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ULocale[] LOCALES = null;
3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // timer for performance analysis
3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long[] times = new long[PATTERNS.length];
3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long timer;
3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (TEST_ALL) {
3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // It may take about an hour for testing all locales
3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            LOCALES = ULocale.getAvailableLocales();
3462d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        } else if (TestFmwk.getExhaustiveness() > 5) {
3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            LOCALES = new ULocale[] {
3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                new ULocale("ar_EG"), new ULocale("bg_BG"), new ULocale("ca_ES"), new ULocale("da_DK"), new ULocale("de"),
3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                new ULocale("de_DE"), new ULocale("el_GR"), new ULocale("en"), new ULocale("en_AU"), new ULocale("en_CA"),
3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                new ULocale("en_US"), new ULocale("es"), new ULocale("es_ES"), new ULocale("es_MX"), new ULocale("fi_FI"),
3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                new ULocale("fr"), new ULocale("fr_CA"), new ULocale("fr_FR"), new ULocale("he_IL"), new ULocale("hu_HU"),
3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                new ULocale("it"), new ULocale("it_IT"), new ULocale("ja"), new ULocale("ja_JP"), new ULocale("ko"),
3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                new ULocale("ko_KR"), new ULocale("nb_NO"), new ULocale("nl_NL"), new ULocale("nn_NO"), new ULocale("pl_PL"),
3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                new ULocale("pt"), new ULocale("pt_BR"), new ULocale("pt_PT"), new ULocale("ru_RU"), new ULocale("sv_SE"),
3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                new ULocale("th_TH"), new ULocale("tr_TR"), new ULocale("zh"), new ULocale("zh_Hans"), new ULocale("zh_Hans_CN"),
3567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                new ULocale("zh_Hant"), new ULocale("zh_Hant_HK"), new ULocale("zh_Hant_TW")
3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            };
3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            LOCALES = new ULocale[] {
3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                new ULocale("en"),
3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            };
3627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        SimpleDateFormat sdfGMT = new SimpleDateFormat(BASEPATTERN);
3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        sdfGMT.setTimeZone(TimeZone.getTimeZone("Etc/GMT"));
3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long testCounts = 0;
3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long[] testTimes = new long[4];
3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean[] expectedRoundTrip = new boolean[4];
3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int testLen = 0;
3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int locidx = 0; locidx < LOCALES.length; locidx++) {
3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            logln("Locale: " + LOCALES[locidx].toString());
3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int patidx = 0; patidx < PATTERNS.length; patidx++) {
3747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                logln("    pattern: " + PATTERNS[patidx]);
3757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String pattern = BASEPATTERN + " " + PATTERNS[patidx];
3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                SimpleDateFormat sdf = new SimpleDateFormat(pattern, LOCALES[locidx]);
3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                boolean minutesOffset = MINUTES_OFFSET.contains(PATTERNS[patidx]);
3787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Set<String> ids = null;
3807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (JDKTZ) {
3817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ids = new TreeSet<String>();
3827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String[] jdkIDs = java.util.TimeZone.getAvailableIDs();
3837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for (String jdkID : jdkIDs) {
3847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (EXCL_TZ_PATTERN.matcher(jdkID).matches()) {
3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            continue;
3867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
3877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        String tmpID = TimeZone.getCanonicalID(jdkID);
3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (tmpID != null) {
3897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            ids.add(tmpID);
3907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
3917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
3927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
3937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ids = TimeZone.getAvailableIDs(SystemTimeZoneType.CANONICAL, null, null);
3947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
3957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (String id : ids) {
3977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (PATTERNS[patidx].equals("V")) {
3987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // Some zones do not have short ID assigned, such as Asia/Riyadh87.
3997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // The time roundtrip will fail for such zones with pattern "V" (short zone ID).
4007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // This is expected behavior.
4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        String shortZoneID = ZoneMeta.getShortID(id);
4027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (shortZoneID == null) {
4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            continue;
4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else if (PATTERNS[patidx].equals("VVV")) {
4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // Some zones are not associated with any region, such as Etc/GMT+8.
4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // The time roundtrip will fail for such zones with pattern "VVV" (exemplar location).
4087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // This is expected behavior.
4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (id.indexOf('/') < 0 || LOC_EXCLUSION_PATTERN.matcher(id).matches()) {
4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            continue;
4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (id.equals("Pacific/Apia") && PATTERNS[patidx].equals("vvvv")
4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            && logKnownIssue("11052", "Ambiguous zone name - Samoa Time")) {
4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        continue;
4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    BasicTimeZone btz = (BasicTimeZone)TimeZone.getTimeZone(id, TimeZone.TIMEZONE_ICU);
4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    TimeZone tz = TimeZone.getTimeZone(id);
4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    sdf.setTimeZone(tz);
4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    long t = START_TIME;
4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    TimeZoneTransition tzt = null;
4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    boolean middle = true;
4262d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    boolean last = false;
4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    while (t < END_TIME) {
4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (tzt == null) {
4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            testTimes[0] = t;
4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            expectedRoundTrip[0] = true;
4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            testLen = 1;
4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else {
4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            int fromOffset = tzt.getFrom().getRawOffset() + tzt.getFrom().getDSTSavings();
4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            int toOffset = tzt.getTo().getRawOffset() + tzt.getTo().getDSTSavings();
4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            int delta = toOffset - fromOffset;
4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (delta < 0) {
4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                boolean isDstDecession = tzt.getFrom().getDSTSavings() > 0 && tzt.getTo().getDSTSavings() == 0;
4387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                testTimes[0] = t + delta - 1;
4397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                expectedRoundTrip[0] = true;
4407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                testTimes[1] = t + delta;
4417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                expectedRoundTrip[1] = isDstDecession ?
4427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        !AMBIGUOUS_DST_DECESSION.contains(PATTERNS[patidx]) :
4437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        !AMBIGUOUS_NEGATIVE_SHIFT.contains(PATTERNS[patidx]);
4447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                testTimes[2] = t - 1;
4457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                expectedRoundTrip[2] = isDstDecession ?
4467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        !AMBIGUOUS_DST_DECESSION.contains(PATTERNS[patidx]) :
4477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        !AMBIGUOUS_NEGATIVE_SHIFT.contains(PATTERNS[patidx]);
4487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                testTimes[3] = t;
4497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                expectedRoundTrip[3] = true;
4507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                testLen = 4;
4517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            } else {
4527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                testTimes[0] = t - 1;
4537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                expectedRoundTrip[0] = true;
4547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                testTimes[1] = t;
4557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                expectedRoundTrip[1] = true;
4567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                testLen = 2;
4577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
4587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
4597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        for (int testidx = 0; testidx < testLen; testidx++) {
4607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            testCounts++;
4617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            timer = System.currentTimeMillis();
4627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            String text = sdf.format(new Date(testTimes[testidx]));
4637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            try {
4647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                Date parsedDate = sdf.parse(text);
4657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                long restime = parsedDate.getTime();
4667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                long timeDiff = restime - testTimes[testidx];
4677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                boolean bTimeMatch = minutesOffset ?
4687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        (timeDiff/60000)*60000 == 0 : timeDiff == 0;
4697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                if (!bTimeMatch) {
4707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    StringBuffer msg = new StringBuffer();
4717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    msg.append("Time round trip failed for ")
4727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        .append("tzid=").append(id)
4737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        .append(", locale=").append(LOCALES[locidx])
4747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        .append(", pattern=").append(PATTERNS[patidx])
4757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        .append(", text=").append(text)
4767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        .append(", gmt=").append(sdfGMT.format(new Date(testTimes[testidx])))
4777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        .append(", time=").append(testTimes[testidx])
4787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        .append(", restime=").append(restime)
4797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        .append(", diff=").append(timeDiff);
4807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    if (expectedRoundTrip[testidx]
4817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            && !isSpecialTimeRoundTripCase(LOCALES[locidx], id, PATTERNS[patidx], testTimes[testidx])) {
4827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        errln("FAIL: " + msg.toString());
4837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    } else if (REALLY_VERBOSE_LOG) {
4847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        logln(msg.toString());
4857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    }
4867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                }
4877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            } catch (ParseException pe) {
4887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                errln("FAIL: " + pe.getMessage() + " tzid=" + id + ", locale=" + LOCALES[locidx] +
4897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        ", pattern=" + PATTERNS[patidx] + ", text=" + text);
4907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
4917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            times[patidx] += System.currentTimeMillis() - timer;
4927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
4932d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
4942d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        if (last) {
4957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            break;
4967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
4972d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
4982d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        tzt = btz.getNextTransition(t, false);
4992d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        if (tzt == null) {
5002d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                            last = true;
5012d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                            t = END_TIME - 1;
5022d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        } else if (middle) {
5037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // Test the date in the middle of two transitions.
5047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            t += (tzt.getTime() - t)/2;
5057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            middle = false;
5067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            tzt = null;
5077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else {
5087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            t = tzt.getTime();
5097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
5107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
5117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
5127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long total = 0;
5167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        logln("### Elapsed time by patterns ###");
5177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < PATTERNS.length; i++) {
5187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            logln(times[i] + "ms (" + PATTERNS[i] + ")");
5197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            total += times[i];
5207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        logln("Total: " + total + "ms");
5227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        logln("Iteration: " + testCounts);
5237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Special exclusions in TestTimeZoneRoundTrip.
5267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // These special cases do not round trip time as designed.
5277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private boolean isSpecialTimeRoundTripCase(ULocale loc, String id, String pattern, long time) {
5287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final Object[][] EXCLUSIONS = {
5297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {null, "Asia/Chita", "zzzz", Long.valueOf(1414252800000L)},
5307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {null, "Asia/Chita", "vvvv", Long.valueOf(1414252800000L)},
5317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {null, "Asia/Srednekolymsk", "zzzz", Long.valueOf(1414241999999L)},
5327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {null, "Asia/Srednekolymsk", "vvvv", Long.valueOf(1414241999999L)},
5337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        };
5347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean isExcluded = false;
5357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (Object[] excl : EXCLUSIONS) {
5362d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            if (excl[0] == null || loc.equals(excl[0])) {
5377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (id.equals(excl[1])) {
5382d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    if (excl[2] == null || pattern.equals(excl[2])) {
5397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (excl[3] == null || ((Long)excl[3]).compareTo(time) == 0) {
5407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            isExcluded = true;
5417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            break;
5427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
5437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
5447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
5457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return isExcluded;
5487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5502d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Test
5517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void TestParse() {
5527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final Object[][] DATA = {
5537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //   text                   inpos       locale      style
5547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //      parseOptions            expected            outpos      time type
5557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"Z",                   0,          "en_US",    Style.ISO_EXTENDED_FULL,
5567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null,                   "Etc/GMT",          1,          TimeType.UNKNOWN},
5577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"Z",                   0,          "en_US",    Style.SPECIFIC_LONG,
5597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null,                   "Etc/GMT",          1,          TimeType.UNKNOWN},
5607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"Zambia time",         0,          "en_US",    Style.ISO_EXTENDED_FULL,
5627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                EnumSet.of(ParseOption.ALL_STYLES), "Etc/GMT",  1,      TimeType.UNKNOWN},
5637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"Zambia time",         0,          "en_US",    Style.GENERIC_LOCATION,
5657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null,                   "Africa/Lusaka",    11,         TimeType.UNKNOWN},
5667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"Zambia time",         0,          "en_US",    Style.ISO_BASIC_LOCAL_FULL,
5687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                EnumSet.of(ParseOption.ALL_STYLES), "Africa/Lusaka",    11, TimeType.UNKNOWN},
5697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"+00:00",              0,          "en_US",    Style.ISO_EXTENDED_FULL,
5717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null,                   "Etc/GMT",          6,          TimeType.UNKNOWN},
5727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"-01:30:45",           0,          "en_US",    Style.ISO_EXTENDED_FULL,
5747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null,                   "GMT-01:30:45",     9,          TimeType.UNKNOWN},
5757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"-7",                  0,          "en_US",    Style.ISO_BASIC_LOCAL_FULL,
5777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null,                   "GMT-07:00",        2,          TimeType.UNKNOWN},
5787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"-2222",               0,          "en_US",    Style.ISO_BASIC_LOCAL_FULL,
5807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null,                   "GMT-22:22",        5,          TimeType.UNKNOWN},
5817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"-3333",               0,          "en_US",    Style.ISO_BASIC_LOCAL_FULL,
5837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null,                   "GMT-03:33",        4,          TimeType.UNKNOWN},
5847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"XXX+01:30YYY",        3,          "en_US",    Style.LOCALIZED_GMT,
5867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null,                   "GMT+01:30",        9,          TimeType.UNKNOWN},
5877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"GMT0",                0,          "en_US",    Style.SPECIFIC_SHORT,
5897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null,                   "Etc/GMT",          3,          TimeType.UNKNOWN},
5907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"EST",                 0,          "en_US",    Style.SPECIFIC_SHORT,
5927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null,                   "America/New_York", 3,          TimeType.STANDARD},
5937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"ESTx",                0,          "en_US",    Style.SPECIFIC_SHORT,
5957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null,                   "America/New_York", 3,          TimeType.STANDARD},
5967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"EDTx",                0,          "en_US",    Style.SPECIFIC_SHORT,
5987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null,                   "America/New_York", 3,          TimeType.DAYLIGHT},
5997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"EST",                 0,          "en_US",    Style.SPECIFIC_LONG,
6017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null,                   null,               0,          TimeType.UNKNOWN},
6027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"EST",                 0,          "en_US",    Style.SPECIFIC_LONG,
6047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                EnumSet.of(ParseOption.ALL_STYLES), "America/New_York", 3,  TimeType.STANDARD},
6057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"EST",                 0,          "en_CA",    Style.SPECIFIC_SHORT,
6077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null,                   "America/Toronto",  3,          TimeType.STANDARD},
6087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"CST",                 0,          "en_US",    Style.SPECIFIC_SHORT,
6107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null,                   "America/Chicago",  3,          TimeType.STANDARD},
6117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"CST",                 0,          "en_GB",    Style.SPECIFIC_SHORT,
6137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null,                   null,               0,          TimeType.UNKNOWN},
6147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"CST",                 0,          "en_GB",    Style.SPECIFIC_SHORT,
6167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                EnumSet.of(ParseOption.TZ_DATABASE_ABBREVIATIONS),  "America/Chicago",  3,  TimeType.STANDARD},
6177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"--CST--",             2,          "en_GB",    Style.SPECIFIC_SHORT,
6197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                EnumSet.of(ParseOption.TZ_DATABASE_ABBREVIATIONS),  "America/Chicago",  5,  TimeType.STANDARD},
6207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"CST",                 0,          "zh_CN",    Style.SPECIFIC_SHORT,
6227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                EnumSet.of(ParseOption.TZ_DATABASE_ABBREVIATIONS),  "Asia/Shanghai",    3,  TimeType.STANDARD},
6237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"AEST",                0,          "en_AU",    Style.SPECIFIC_SHORT,
6257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                EnumSet.of(ParseOption.TZ_DATABASE_ABBREVIATIONS),  "Australia/Sydney", 4,  TimeType.STANDARD},
6267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"AST",                 0,          "ar_SA",    Style.SPECIFIC_SHORT,
6287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                EnumSet.of(ParseOption.TZ_DATABASE_ABBREVIATIONS),  "Asia/Riyadh",      3,  TimeType.STANDARD},
6297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"AQTST",               0,          "en",       Style.SPECIFIC_LONG,
6317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null,                       null,           0,          TimeType.UNKNOWN},
6327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"AQTST",           0,      "en",       Style.SPECIFIC_LONG,
6347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                EnumSet.of(ParseOption.ALL_STYLES), null,   0,          TimeType.UNKNOWN},
6357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {"AQTST",           0,      "en",       Style.SPECIFIC_LONG,
6377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                EnumSet.of(ParseOption.ALL_STYLES, ParseOption.TZ_DATABASE_ABBREVIATIONS),  "Asia/Aqtobe",  5,  TimeType.DAYLIGHT},
6382d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
6392d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            {"hora de verano británica", 0,     "es",       Style.SPECIFIC_LONG,
6402d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                null,                   "Europe/London",    24,         TimeType.DAYLIGHT},
6417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        };
6427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (Object[] test : DATA) {
6447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String text = (String)test[0];
6457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int inPos = (Integer)test[1];
6467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ULocale loc = new ULocale((String)test[2]);
6477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Style style = (Style)test[3];
6487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            EnumSet<ParseOption> options = (EnumSet<ParseOption>)test[4];
6497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String expID = (String)test[5];
6507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int expPos = (Integer)test[6];
6517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            TimeType expType = (TimeType)test[7];
6527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            TimeZoneFormat tzfmt = TimeZoneFormat.getInstance(loc);
6547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Output<TimeType> timeType = new Output<TimeType>(TimeType.UNKNOWN);
6557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ParsePosition pos = new ParsePosition(inPos);
6567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            TimeZone tz = tzfmt.parse(style, text, pos, options, timeType);
6577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String errMsg = null;
6597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (tz == null) {
6607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (expID != null) {
6617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    errMsg = "Parse failure - expected: " + expID;
6627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
6637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (!tz.getID().equals(expID)) {
6647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                errMsg = "Time zone ID: " + tz.getID() + " - expected: " + expID;
6657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (pos.getIndex() != expPos) {
6667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                errMsg = "Parsed pos: " + pos.getIndex() + " - expected: " + expPos;
6677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (timeType.value != expType) {
6687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                errMsg = "Time type: " + timeType + " - expected: " + expType;
6697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
6707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (errMsg != null) {
672bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                errln("Fail: " + errMsg +
673bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        " [text=" + text + ", pos=" + inPos +
674bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                        ", locale=" + loc + ", style=" + style + "]");
6757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
6767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6782d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
679e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer    // Coverage tests for other versions of the parse() method. All of them end up
680e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer    // calling the full parse() method tested on the TestParse() test.
681e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer    public void TestParseCoverage() {
6822d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        TimeZone expectedTZ = TimeZone.getTimeZone("America/Los_Angeles");
683e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        TimeZoneFormat fmt = TimeZoneFormat.getInstance(ULocale.ENGLISH);
6842d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
685e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        // Test parse(String)
686e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        try {
687e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer            TimeZone tz1 = fmt.parse("America/Los_Angeles");
688e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer            if (tz1 == null) {
689e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer                errln("Parse failure using parse(String) - expected: " + expectedTZ.getID());
690e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer            } else if (!expectedTZ.equals(tz1)) {
691e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer                errln("Parsed TimeZone: '" + tz1.getID()  + "' using parse(String) - expected: "
692e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer                        + expectedTZ.getID());
6932d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            }
694e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        } catch (ParseException e) {
695e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer            errln("Parse failure using parse(String) - expected: " + expectedTZ.getID()
696e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer                    + " exception: " + e.getMessage());
697e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        }
6982d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
699e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        // Test parse(String, ParsePosition)
700e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        TimeZone tz2 = fmt.parse("++America/Los_Angeles", new ParsePosition(2));
701e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        if (tz2 == null) {
7022d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            errln("Parse failure using parse(String, ParsePosition) - expected: "
703e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer                    + expectedTZ.getID());
704e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        } else if (!expectedTZ.equals(tz2)) {
705e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer            errln("Parsed TimeZone: '" + tz2.getID()  + "' using parse(String, ParsePosition) - expected: "
706e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer                    + expectedTZ.getID());
707e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        }
7082d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
709e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        // Test parseObject(String, ParsePosition)
710e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        Object tz3 = fmt.parseObject("++America/Los_Angeles", new ParsePosition(2));
711e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        if (tz3 == null) {
7122d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            errln("Parse failure using parseObject(String, ParsePosition) - expected: "
713e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer                    + expectedTZ.getID());
714e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        } else if (!expectedTZ.equals(tz3)) {
715e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer            errln("Parsed TimeZone: '" + ((TimeZone)tz3).getID()
716e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer                    + "' using parseObject(String, ParsePosition) - expected: "
717e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer                    + expectedTZ.getID());
718e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        }
719e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer    }
7207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7212d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Test
7227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void TestISOFormat() {
7237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final int[] OFFSET = {
7247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            0,          // 0
7257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            999,        // 0.999s
7267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            -59999,     // -59.999s
7277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            60000,      // 1m
7287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            -77777,     // -1m 17.777s
7297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            1800000,    // 30m
7307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            -3600000,   // -1h
7317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            36000000,   // 10h
7327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            -37800000,  // -10h 30m
7337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            -37845000,  // -10h 30m 45s
7347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            108000000,  // 30h
7357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        };
7362d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
7377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final String[][] ISO_STR = {
7387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // 0
7397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
7407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "Z", "Z", "Z", "Z", "Z",
7417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "+00", "+0000", "+00:00", "+0000", "+00:00",
7427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "+0000"
7437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
7447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // 999
7457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
7467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "Z", "Z", "Z", "Z", "Z",
7477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "+00", "+0000", "+00:00", "+0000", "+00:00",
7487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "+0000"
7497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
7507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // -59999
7517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
7527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "Z", "Z", "Z", "-000059", "-00:00:59",
7537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "+00", "+0000", "+00:00", "-000059", "-00:00:59",
7547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "-000059"
7557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
7567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // 60000
7577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
7587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "+0001", "+0001", "+00:01", "+0001", "+00:01",
7597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "+0001", "+0001", "+00:01", "+0001", "+00:01",
7607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "+0001"
7617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
7627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // -77777
7637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
7647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "-0001", "-0001", "-00:01", "-000117", "-00:01:17",
7657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "-0001", "-0001", "-00:01", "-000117", "-00:01:17",
7667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "-000117"
7677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
7687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // 1800000
7697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
7707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "+0030", "+0030", "+00:30", "+0030", "+00:30",
7717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "+0030", "+0030", "+00:30", "+0030", "+00:30",
7727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "+0030"
7737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
7747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // -3600000
7757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
7767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "-01", "-0100", "-01:00", "-0100", "-01:00",
7777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "-01", "-0100", "-01:00", "-0100", "-01:00",
7787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "-0100"
7797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
7807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // 36000000
7817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
7827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "+10", "+1000", "+10:00", "+1000", "+10:00",
7837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "+10", "+1000", "+10:00", "+1000", "+10:00",
7847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "+1000"
7857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
7867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // -37800000
7877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
7887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "-1030", "-1030", "-10:30", "-1030", "-10:30",
7897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "-1030", "-1030", "-10:30", "-1030", "-10:30",
7907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "-1030"
7917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
7927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // -37845000
7937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
7947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "-1030", "-1030", "-10:30", "-103045", "-10:30:45",
7957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "-1030", "-1030", "-10:30", "-103045", "-10:30:45",
7967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "-103045"
7977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
7987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // 108000000
7997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
8007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null, null, null, null, null,
8017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null, null, null, null, null,
8027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                null
8037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
8047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        };
8057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final String[] PATTERN = {
8077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            "X", "XX", "XXX", "XXXX", "XXXXX", "x", "xx", "xxx", "xxxx", "xxxxx",
8087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            "Z", // equivalent to "xxxx"
8097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        };
8107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final int[] MIN_OFFSET_UNIT = {
8127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            60000, 60000, 60000, 1000, 1000, 60000, 60000, 60000, 1000, 1000,
8137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            1000,
8147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        };
8157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Formatting
8177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        SimpleDateFormat sdf = new SimpleDateFormat();
8187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Date d = new Date();
8197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < OFFSET.length; i++) {
8217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            SimpleTimeZone tz = new SimpleTimeZone(OFFSET[i], "Zone Offset:" + String.valueOf(OFFSET[i]) + "ms");
8227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            sdf.setTimeZone(tz);
8237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int j = 0; j < PATTERN.length; j++) {
8247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                sdf.applyPattern(PATTERN[j]);
8257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                try {
8267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String result = sdf.format(d);
8277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (!result.equals(ISO_STR[i][j])) {
8287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        errln("FAIL: pattern=" + PATTERN[j] + ", offset=" + OFFSET[i] + " -> "
8297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            + result + " (expected: " + ISO_STR[i][j] + ")");
8307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
8317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (IllegalArgumentException e) {
8327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (ISO_STR[i][j] != null) {
8337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        errln("FAIL: IAE thrown for pattern=" + PATTERN[j] + ", offset=" + OFFSET[i]
8347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                + " (expected: " + ISO_STR[i][j] + ")");
8357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
8367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
8377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
8387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Parsing
8417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        SimpleTimeZone bogusTZ = new SimpleTimeZone(-1, "Zone Offset: -1ms");
8427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < ISO_STR.length; i++) {
8437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int j = 0; j < ISO_STR[i].length; j++) {
8447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (ISO_STR[i][j] == null) {
8457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    continue;
8467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
8477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ParsePosition pos = new ParsePosition(0);
8487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Calendar outcal = Calendar.getInstance(bogusTZ);
8497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                sdf.applyPattern(PATTERN[j]);
8507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                sdf.parse(ISO_STR[i][j], outcal, pos);
8527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (pos.getIndex() != ISO_STR[i][j].length()) {
8547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    errln("FAIL: Failed to parse the entire input string: " + ISO_STR[i][j]);
8557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    continue;
8567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
8577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeZone outtz = outcal.getTimeZone();
8597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int outOffset = outtz.getRawOffset();
8607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int adjustedOffset = OFFSET[i] / MIN_OFFSET_UNIT[j] * MIN_OFFSET_UNIT[j];
8617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (outOffset != adjustedOffset) {
8637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    errln("FAIL: Incorrect offset:" + outOffset + "ms for input string: " + ISO_STR[i][j]
8647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            + " (expected:" + adjustedOffset + "ms)");
8657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
8667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
8677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8702d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Test
8717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void TestFormat() {
8727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final Date dateJan = new Date(1358208000000L);  // 2013-01-15T00:00:00Z
8737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final Date dateJul = new Date(1373846400000L);  // 2013-07-15T00:00:00Z
8747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final Object[][] TESTDATA = {
8767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
8777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "en",
8782d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                "America/Los_Angeles",
8797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateJan,
8807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Style.GENERIC_LOCATION,
8817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "Los Angeles Time",
8827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeType.UNKNOWN
8837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
8847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
8857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "en",
8867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "America/Los_Angeles",
8877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateJan,
8887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Style.GENERIC_LONG,
8897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "Pacific Time",
8907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeType.UNKNOWN
8917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
8927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
8937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "en",
8947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "America/Los_Angeles",
8957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateJan,
8967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Style.SPECIFIC_LONG,
8977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "Pacific Standard Time",
8987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeType.STANDARD
8997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
9007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
9017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "en",
9027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "America/Los_Angeles",
9037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateJul,
9047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Style.SPECIFIC_LONG,
9057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "Pacific Daylight Time",
9067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeType.DAYLIGHT
9077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
9087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
9097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "ja",
9107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "America/Los_Angeles",
9117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateJan,
9127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Style.ZONE_ID,
9137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "America/Los_Angeles",
9147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeType.UNKNOWN
9157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
9167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
9177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "fr",
9187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "America/Los_Angeles",
9197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateJul,
9207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Style.ZONE_ID_SHORT,
9217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "uslax",
9227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeType.UNKNOWN
9237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
9247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
9257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "en",
9267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "America/Los_Angeles",
9277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateJan,
9287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Style.EXEMPLAR_LOCATION,
9297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "Los Angeles",
9307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeType.UNKNOWN
9317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
9327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
9337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "ja",
9347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "Asia/Tokyo",
9357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateJan,
9367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Style.GENERIC_LONG,
9377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "\u65E5\u672C\u6A19\u6E96\u6642",   // "日本標準時"
9387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeType.UNKNOWN
9397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
9407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        };
9417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (Object[] testCase : TESTDATA) {
9437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            TimeZone tz = TimeZone.getTimeZone((String)testCase[1]);
9447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Output<TimeType> timeType = new Output<TimeType>();
9457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ULocale uloc = new ULocale((String)testCase[0]);
9477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            TimeZoneFormat tzfmt = TimeZoneFormat.getInstance(uloc);
9487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String out = tzfmt.format((Style)testCase[3], tz, ((Date)testCase[2]).getTime(), timeType);
9497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9502d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            if (!out.equals(testCase[4]) || timeType.value != testCase[5]) {
9517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                errln("Format result for [locale=" + testCase[0] + ",tzid=" + testCase[1] + ",date=" + testCase[2]
9527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        + ",style=" + testCase[3] + "]: expected [output=" + testCase[4] + ",type=" + testCase[5]
9537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        + "]; actual [output=" + out + ",type=" + timeType.value + "]");
9547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
9557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // with equivalent Java Locale
9577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Locale loc = uloc.toLocale();
9587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            tzfmt = TimeZoneFormat.getInstance(loc);
9597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            out = tzfmt.format((Style)testCase[3], tz, ((Date)testCase[2]).getTime(), timeType);
9607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9612d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            if (!out.equals(testCase[4]) || timeType.value != testCase[5]) {
9627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                errln("Format result for [locale(Java)=" + testCase[0] + ",tzid=" + testCase[1] + ",date=" + testCase[2]
9637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        + ",style=" + testCase[3] + "]: expected [output=" + testCase[4] + ",type=" + testCase[5]
9647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        + "]; actual [output=" + out + ",type=" + timeType.value + "]");
9657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
9667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9692d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Test
9707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void TestFormatTZDBNames() {
9717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final Date dateJan = new Date(1358208000000L);  // 2013-01-15T00:00:00Z
9727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final Date dateJul = new Date(1373846400000L);  // 2013-07-15T00:00:00Z
9737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final Object[][] TESTDATA = {
9757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
9767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "en",
9772d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                "America/Chicago",
9787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateJan,
9797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Style.SPECIFIC_SHORT,
9807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "CST",
9817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeType.STANDARD
9827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
9837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
9847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "en",
9852d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                "Asia/Shanghai",
9867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateJan,
9877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Style.SPECIFIC_SHORT,
9887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "CST",
9897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeType.STANDARD
9907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
9917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
9927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "zh_Hans",
9932d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                "Asia/Shanghai",
9947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateJan,
9957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Style.SPECIFIC_SHORT,
9967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "CST",
9977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeType.STANDARD
9987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
9997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
10007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "en",
10017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "America/Los_Angeles",
10027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateJul,
10037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Style.SPECIFIC_LONG,
10047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "GMT-07:00",    // No long display names
10057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeType.DAYLIGHT
10067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
10077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
10087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "ja",
10097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "America/Los_Angeles",
10107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateJul,
10117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Style.SPECIFIC_SHORT,
10127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "PDT",
10137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeType.DAYLIGHT
10147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
10157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
10167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "en",
10177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "Australia/Sydney",
10187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateJan,
10197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Style.SPECIFIC_SHORT,
10207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "AEDT",
10217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeType.DAYLIGHT
10227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
10237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
10247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "en",
10257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "Australia/Sydney",
10267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateJul,
10277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Style.SPECIFIC_SHORT,
10287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "AEST",
10297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeType.STANDARD
10307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            },
10317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        };
10327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (Object[] testCase : TESTDATA) {
10347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ULocale loc = new ULocale((String)testCase[0]);
10357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            TimeZoneFormat tzfmt = TimeZoneFormat.getInstance(loc).cloneAsThawed();
10367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            TimeZoneNames tzdbNames = TimeZoneNames.getTZDBInstance(loc);
10377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            tzfmt.setTimeZoneNames(tzdbNames);
10387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            TimeZone tz = TimeZone.getTimeZone((String)testCase[1]);
10407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Output<TimeType> timeType = new Output<TimeType>();
10417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String out = tzfmt.format((Style)testCase[3], tz, ((Date)testCase[2]).getTime(), timeType);
10427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10432d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            if (!out.equals(testCase[4]) || timeType.value != testCase[5]) {
10447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                errln("Format result for [locale=" + testCase[0] + ",tzid=" + testCase[1] + ",date=" + testCase[2]
10457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        + ",style=" + testCase[3] + "]: expected [output=" + testCase[4] + ",type=" + testCase[5]
10467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        + "]; actual [output=" + out + ",type=" + timeType.value + "]");
10477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1050f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
1051e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer    // Tests format(Object, StringBuffer, FieldPosition):StringBuffer method
1052e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer    // inherited from Format class
1053e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer    public void TestInheritedFormat() {
1054e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
1055e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        Calendar cal = Calendar.getInstance(tz);
1056e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        cal.setTimeInMillis(1459187377690L); // Mar 28, 2016
10572d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
1058e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        StringBuffer sb = new StringBuffer();
1059e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        FieldPosition fp = new FieldPosition(DateFormat.Field.TIME_ZONE);
10602d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
1061e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        TimeZoneFormat fmt = TimeZoneFormat.getInstance(ULocale.ENGLISH);
10622d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
1063e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        // Test formatting a non-timezone related object
1064e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        try {
1065e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer            fmt.format(new Object(), sb, fp);
1066e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer            errln("ERROR: format non-timezone related object failed");
1067e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        } catch (IllegalArgumentException e) { /* Expected */ }
10682d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
1069e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        // Test formatting a TimeZone object
1070e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        sb = new StringBuffer();
1071e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        fmt.format(tz, sb, fp);
1072e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        // When formatting a TimeZone object the formatter uses the current date.
1073e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        String fmtOutput = tz.inDaylightTime(new Date()) ? "GMT-07:00" : "GMT-08:00";
1074e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        if (!sb.toString().equals(fmtOutput)) {
1075e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer            errln("ERROR: format TimerZone object failed. Expected: " + fmtOutput + ", actual: " + sb);
1076e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        }
10772d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
1078e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        // Test formatting a Calendar object
1079e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        sb = new StringBuffer();
1080e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        fmt.format(cal, sb, fp);
1081e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        if (!sb.toString().equals("GMT-07:00")) {
1082e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer            errln("ERROR: format Calendar object failed. Expected: GMT-07:00, actual: " + sb);
1083e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        }
1084e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer    }
10852d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
1086f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    // This is a test case of Ticket#11487.
1087f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    // Because the problem is reproduced for the very first time,
1088f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    // the reported problem cannot be reproduced with regular test
1089f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    // execution. Run this test alone reproduced the problem before
1090f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    // the fix was merged.
10912d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Test
1092f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    public void TestTZDBNamesThreading() {
1093f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        final TZDBTimeZoneNames names = new TZDBTimeZoneNames(ULocale.ENGLISH);
1094f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        final AtomicInteger found = new AtomicInteger();
1095f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        List<Thread> threads = new ArrayList<Thread>();
1096f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        final int numIteration = 1000;
1097f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
1098f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        try {
1099f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            for (int i = 0; i < numIteration; i++) {
1100f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                Thread thread = new Thread() {
1101f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    @Override
1102f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    public void run() {
1103f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        int resultSize = names.find("GMT", 0, EnumSet.allOf(NameType.class)).size();
1104f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        if (resultSize > 0) {
1105f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            found.incrementAndGet();
1106f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        }
1107f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    }
1108f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                };
1109f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                thread.start();
1110f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                threads.add(thread);
1111f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            }
1112f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
1113f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            for(Thread thread: threads) {
1114f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                thread.join();
1115f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            }
1116f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        } catch (Throwable t) {
1117f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            errln(t.toString());
1118f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        }
1119f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
1120f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        if (found.intValue() != numIteration) {
1121f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            errln("Incorrect count: " + found.toString() + ", expected: " + numIteration);
1122f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        }
1123f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    }
11242d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
11252d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Test
11262d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    public void TestGetDisplayNames() {
11272d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        long date = System.currentTimeMillis();
11282d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        NameType[] types = new NameType[]{
11292d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                NameType.LONG_STANDARD, NameType.LONG_DAYLIGHT,
11302d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                NameType.SHORT_STANDARD, NameType.SHORT_DAYLIGHT
11312d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        };
11322d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        Set<String> zones = ZoneMeta.getAvailableIDs(SystemTimeZoneType.ANY, null, null);
11332d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
11342d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        int casesTested = 0;
11352d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        Random rnd = new Random(2016);
11362d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        for (ULocale uloc : ULocale.getAvailableLocales()) {
11372d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            if (rnd.nextDouble() > 0.01) { continue; }
11382d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            for (String zone : zones) {
11392d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                if (rnd.nextDouble() > 0.01) { continue; }
11402d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                casesTested++;
11412d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
11422d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                // Test default TimeZoneNames (uses an overridden getDisplayNames)
11432d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                {
11442d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    TimeZoneNames tznames = TimeZoneNames.getInstance(uloc);
11452d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    tznames.loadAllDisplayNames();
11462d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    String[] result = new String[types.length];
11472d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    tznames.getDisplayNames(zone, types, date, result, 0);
11482d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    for (int i=0; i<types.length; i++) {
11492d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        NameType type = types[i];
11502d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        String expected = result[i];
11512d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        String actual = tznames.getDisplayName(zone, type, date);
11522d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        assertEquals("TimeZoneNames: getDisplayNames() returns different result than getDisplayName()"
11532d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                + " for " + zone + " in locale " + uloc, expected, actual);
11542d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    }
11552d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    // Coverage for empty call to getDisplayNames
11562d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    tznames.getDisplayNames(null, null, 0, null, 0);
11572d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                }
11582d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
11592d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                // Test TZDBTimeZoneNames (uses getDisplayNames from abstract class)
11602d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                {
11612d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    TimeZoneNames tznames = new TZDBTimeZoneNames(uloc);
11622d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    tznames.loadAllDisplayNames();
11632d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    String[] result = new String[types.length];
11642d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    tznames.getDisplayNames(zone, types, date, result, 0);
11652d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    for (int i=0; i<types.length; i++) {
11662d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        NameType type = types[i];
11672d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        String expected = result[i];
11682d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        String actual = tznames.getDisplayName(zone, type, date);
11692d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        assertEquals("TZDBTimeZoneNames: getDisplayNames() returns different result than getDisplayName()"
11702d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                + " for " + zone + " in locale " + uloc, expected, actual);
11712d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    }
11722d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    // Coverage for empty call to getDisplayNames
11732d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    tznames.getDisplayNames(null, null, 0, null, 0);
11742d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                }
11752d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            }
11762d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        }
11772d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
11782d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        assertTrue("No cases were tested", casesTested > 0);
11792d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    }
11802d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
11812d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    class TimeZoneNamesInheriter extends TimeZoneNames {
11822d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        private static final long serialVersionUID = 1L;
11832d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
11842d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
11852d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        public Set<String> getAvailableMetaZoneIDs() {
11862d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            return null;
11872d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        }
11882d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
11892d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
11902d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        public Set<String> getAvailableMetaZoneIDs(String tzID) {
11912d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            return null;
11922d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        }
11932d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
11942d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
11952d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        public String getMetaZoneID(String tzID, long date) {
11962d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            return null;
11972d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        }
11982d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
11992d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
12002d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        public String getReferenceZoneID(String mzID, String region) {
12012d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            return null;
12022d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        }
12032d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
12042d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
12052d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        public String getMetaZoneDisplayName(String mzID, NameType type) {
12062d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            return null;
12072d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        }
12082d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
12092d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
12102d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        public String getTimeZoneDisplayName(String tzID, NameType type) {
12112d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            return null;
12122d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        }
12132d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    }
12142d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
12152d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    // Coverage for default implementation and abstract methods in base class.
12162d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Test
12172d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    public void TestDefaultTimeZoneNames() {
12182d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        long date = System.currentTimeMillis();
12192d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        TimeZoneNames.Factory factory;
12202d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        try {
12212d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            Class cls = Class.forName("com.ibm.icu.text.TimeZoneNames$DefaultTimeZoneNames$FactoryImpl");
12222d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            factory = (Factory) cls.newInstance();
12232d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        } catch (Exception e) {
12242d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            errln("Could not create class DefaultTimeZoneNames.FactoryImpl: " + e.getClass() + ": " + e.getMessage());
12252d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            return;
12262d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        }
12272d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        TimeZoneNames tzn = factory.getTimeZoneNames(ULocale.ENGLISH);
12282d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        assertEquals("Abstract: getAvailableMetaZoneIDs()",
12292d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                tzn.getAvailableMetaZoneIDs(), Collections.emptySet());
12302d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        assertEquals("Abstract: getAvailableMetaZoneIDs(String tzID)",
12312d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                tzn.getAvailableMetaZoneIDs("America/Chicago"), Collections.emptySet());
12322d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        assertEquals("Abstract: getMetaZoneID(String tzID, long date)",
12332d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                tzn.getMetaZoneID("America/Chicago", date), null);
12342d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        assertEquals("Abstract: getReferenceZoneID(String mzID, String region)",
12352d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                tzn.getReferenceZoneID("America_Central", "IT"), null);
12362d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        assertEquals("Abstract: getMetaZoneDisplayName(String mzID, NameType type)",
12372d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                tzn.getMetaZoneDisplayName("America_Central", NameType.LONG_DAYLIGHT), null);
12382d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        assertEquals("Abstract: getTimeZoneDisplayName(String mzID, NameType type)",
12392d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                tzn.getTimeZoneDisplayName("America/Chicago", NameType.LONG_DAYLIGHT), null);
12402d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        assertEquals("Abstract: find(CharSequence text, int start, EnumSet<NameType> nameTypes)",
12412d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                tzn.find("foo", 0, EnumSet.noneOf(NameType.class)), Collections.emptyList());
12422d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
12432d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        // Other abstract-class methods that aren't covered
12442d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        tzn = new TimeZoneNamesInheriter();
12452d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        try {
12462d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            tzn.find(null, 0, null);
12472d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        } catch (UnsupportedOperationException e) {
12482d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            assertEquals("find() exception", "The method is not implemented in TimeZoneNames base class.", e.getMessage());
12492d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        }
12502d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    }
12512d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
1252e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer    // Basic get/set test for methods not being called otherwise.
12532d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Test
1254e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer    public void TestAPI() {
1255e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        TimeZoneFormat tzfmtEn = TimeZoneFormat.getInstance(ULocale.ENGLISH);
1256e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        TimeZoneFormat tzfmtAr = TimeZoneFormat.getInstance(new ULocale("ar")).cloneAsThawed();
12572d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        TimeZoneNames tzn = TimeZoneNames.getInstance(Locale.ENGLISH);
12582d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
1259e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        String digits = tzfmtEn.getGMTOffsetDigits();
1260e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        tzfmtAr.setGMTOffsetDigits(digits);
1261e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        if (!digits.equals(tzfmtAr.getGMTOffsetDigits())) {
1262e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer            errln("ERROR: get/set GMTOffsetDigits failed");
1263e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        }
12642d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
1265e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        String pattern = tzfmtEn.getGMTOffsetPattern(GMTOffsetPatternType.POSITIVE_H);
1266e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        tzfmtAr.setGMTOffsetPattern(GMTOffsetPatternType.POSITIVE_H, pattern);
1267e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        if (!pattern.equals(tzfmtAr.getGMTOffsetPattern(GMTOffsetPatternType.POSITIVE_H))) {
1268e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer            errln("ERROR: get/set GMTOffsetPattern failed");
1269e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        }
12702d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
1271e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        String zeroFmt = tzfmtEn.getGMTZeroFormat();
1272e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        tzfmtAr.setGMTZeroFormat(zeroFmt);
1273e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        if (!zeroFmt.equals(tzfmtAr.getGMTZeroFormat())) {
1274e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer            errln("ERROR: get/set GMTZeroFormat failed");
1275e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer        }
12762d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
12772d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        Set<String> allAvailableMZIDs = tzn.getAvailableMetaZoneIDs();
12782d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        if (allAvailableMZIDs.size() < 150 || !allAvailableMZIDs.contains("America_Central")) {
12792d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            errln("ERROR: getAvailableMetaZoneIDs() did not return expected value");
12802d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        }
12812d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
12822d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        Set<String> kinshasaAvailableMZIDs = tzn.getAvailableMetaZoneIDs("Africa/Kinshasa");
12832d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        if (!kinshasaAvailableMZIDs.contains("Africa_Western") || kinshasaAvailableMZIDs.contains("America_Central")) {
12842d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            errln("ERROR: getAvailableMetaZoneIDs('Africa/Kinshasa') did not return expected value");
12852d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        }
12862d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
12872d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        try {
12882d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            new TimeZoneNames.MatchInfo(null, null, null, -1);
12892d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            assertTrue("MatchInfo doesn't throw IllegalArgumentException", false);
12902d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        } catch (IllegalArgumentException e) {
12912d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            assertEquals("MatchInfo constructor exception", "nameType is null", e.getMessage());
12922d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        }
12932d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
12942d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        try {
12952d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            new TimeZoneNames.MatchInfo(NameType.LONG_GENERIC, null, null, -1);
12962d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            assertTrue("MatchInfo doesn't throw IllegalArgumentException", false);
12972d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        } catch (IllegalArgumentException e) {
12982d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            assertEquals("MatchInfo constructor exception", "Either tzID or mzID must be available", e.getMessage());
12992d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        }
13002d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
13012d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        try {
13022d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            new TimeZoneNames.MatchInfo(NameType.LONG_GENERIC, "America/Chicago", null, -1);
13032d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            assertTrue("MatchInfo doesn't throw IllegalArgumentException", false);
13042d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        } catch (IllegalArgumentException e) {
13052d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            assertEquals("MatchInfo constructor exception", "matchLength must be positive value", e.getMessage());
13062d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        }
1307e7a7efded856b8959849879938d1cb2e8ccab318Markus Scherer    }
13082d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert}
1309