test/jdk/java/text/Format/DateFormat/SDFTCKZoneNamesTest.java
author naoto
Thu, 07 Mar 2019 12:56:48 -0800
changeset 54026 3e7fd3fe8411
permissions -rw-r--r--
8218948: SimpleDateFormat :: format - Zone Names are not reflected correctly during run time Reviewed-by: lancea, rgoel

/*
 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/**
 * @test
 * @bug 8218948
 * @summary TCK tests that check the time zone names between DFS.getZoneStrings()
 *      and SDF.format("z*")
 * @run main SDFTCKZoneNamesTest
 */
import java.text.*;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

public class SDFTCKZoneNamesTest {

    StringBuffer myFormat(Date date, SimpleDateFormat sdf) {
        String pattern = sdf.toPattern();
        StringBuffer toAppendTo = new StringBuffer("");
        boolean inQuote = false;
        char prevCh = 0;
        char ch;
        int count = 0;
        for (int i = 0; i < pattern.length(); i++) {
            ch = pattern.charAt(i);
            if (inQuote) {
                if (ch == '\'') {
                    inQuote = false;
                    if (count == 0) toAppendTo.append(ch);
                    else count = 0;
                } else {
                    toAppendTo.append(ch);
                    count++;
                }
            } else { // not inQuote
                if (ch == '\'') {
                    inQuote = true;
                    if (count > 0) {
                        toAppendTo.append(subFormat(prevCh, count, date, sdf));
                        count = 0;
                        prevCh = 0;
                    }
                } else if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z') {
                    if (ch != prevCh && count > 0) {
                        toAppendTo.append(subFormat(prevCh, count, date, sdf));
                        prevCh = ch;
                        count = 1;
                    } else {
                        if (ch != prevCh) prevCh = ch;
                        count++;
                    }
                } else if (count > 0) {
                    toAppendTo.append(subFormat(prevCh, count, date, sdf));
                    toAppendTo.append(ch);
                    prevCh = 0;
                    count = 0;
                } else toAppendTo.append(ch);
            }
        }
        if (count > 0) {
            toAppendTo.append(subFormat(prevCh, count, date, sdf));
        }
        return toAppendTo;
    }

    private String subFormat(char ch, int count, Date date, SimpleDateFormat sdf)
            throws IllegalArgumentException {
        int value = 0;
        int patternCharIndex = -1;
        int maxIntCount = 10;
        String current = "";
        DateFormatSymbols formatData = sdf.getDateFormatSymbols();
        Calendar calendar = sdf.getCalendar();
        calendar.setTime(date);
        NumberFormat nf = sdf.getNumberFormat();
        nf.setGroupingUsed(false);

        if ((patternCharIndex = "GyMdkHmsSEDFwWahKz".indexOf(ch)) == -1)
            throw new IllegalArgumentException("Illegal pattern character " +
                    "'" + ch + "'");
        switch (patternCharIndex) {
            case 0: // 'G' - ERA
                value = calendar.get(Calendar.ERA);
                current = formatData.getEras()[value];
                break;
            case 1: // 'y' - YEAR
                value = calendar.get(Calendar.YEAR);

                if (count == 2) {
                    // For formatting, if the number of pattern letters is 2,
                    // the year is truncated to 2 digits;
                    current = zeroPaddingNumber(value, 2, 2, nf);
                } else {
                    // otherwise it is interpreted as a number.
                    current = zeroPaddingNumber(value, count, maxIntCount, nf);
                }

                break;
            case 2: // 'M' - MONTH
                value = calendar.get(Calendar.MONTH);
                if (count >= 4)
                    // DateFormatSymbols::getMonths spec: "If the language requires different forms for formatting
                    // and stand-alone usages, this method returns month names in the formatting form."
                    // Because of that only formatting cases patterns may be tested. Like, "MMMM yyyy". Wrong
                    // pattern: "MMMM".
                    current = formatData.getMonths()[value];
                else if (count == 3)
                    // DateFormatSymbols::getShortMonths spec: "If the language requires different forms for formatting
                    // and stand-alone usages, This method returns short month names in the formatting form."
                    // Because of that only formatting cases patterns may be tested. Like, "MMM yyyy". Wrong pattern:
                    // "MMM".
                    current = formatData.getShortMonths()[value];
                else
                    current = zeroPaddingNumber(value + 1, count, maxIntCount, nf);
                break;
            case 3: // 'd' - DATE
                value = calendar.get(Calendar.DATE);
                current = zeroPaddingNumber(value, count, maxIntCount, nf);
                break;
            case 4: // 'k' - HOUR_OF_DAY: 1-based.  eg, 23:59 + 1 hour =>> 24:59
                if ((value = calendar.get(Calendar.HOUR_OF_DAY)) == 0)
                    current = zeroPaddingNumber(
                            calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1,
                            count, maxIntCount, nf);
                else
                    current = zeroPaddingNumber(value, count, maxIntCount, nf);
                break;
            case 5: // 'H' - HOUR_OF_DAY:0-based.  eg, 23:59 + 1 hour =>> 00:59
                value = calendar.get(Calendar.HOUR_OF_DAY);
                current = zeroPaddingNumber(value, count, maxIntCount, nf);
                break;
            case 6: // 'm' - MINUTE
                value = calendar.get(Calendar.MINUTE);
                current = zeroPaddingNumber(value, count, maxIntCount, nf);
                break;
            case 7: // 's' - SECOND
                value = calendar.get(Calendar.SECOND);
                current = zeroPaddingNumber(value, count, maxIntCount, nf);
                break;
            case 8: // 'S' - MILLISECOND
                value = calendar.get(Calendar.MILLISECOND);
        /*
        if (count > 3)
            value = value * (int) Math.pow(10, count - 3);
        else if (count == 2)
            value = (value + 5) / 10;
        else if (count == 1)
            value = (value + 50) / 100;
        */
                current = zeroPaddingNumber(value, count, maxIntCount, nf);
                break;
            case 9: // 'E' - DAY_OF_WEEK
                value = calendar.get(Calendar.DAY_OF_WEEK);
                if (count >= 4)
                    current = formatData.getWeekdays()[value];
                else // count < 4, use abbreviated form if exists
                    current = formatData.getShortWeekdays()[value];
                break;
            case 10:    // 'D' - DAY_OF_YEAR
                value = calendar.get(Calendar.DAY_OF_YEAR);
                current = zeroPaddingNumber(value, count, maxIntCount, nf);
                break;
            case 11:   // 'F' - DAY_OF_WEEK_IN_MONTH
                value = calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH);
                current = zeroPaddingNumber(value, count, maxIntCount, nf);
                break;
            case 12:    // 'w' - WEEK_OF_YEAR
                value = calendar.get(Calendar.WEEK_OF_YEAR);
                current = zeroPaddingNumber(value, count, maxIntCount, nf);
                break;
            case 13:    // 'W' - WEEK_OF_MONTH
                value = calendar.get(Calendar.WEEK_OF_MONTH);
                current = zeroPaddingNumber(value, count, maxIntCount, nf);
                break;
            case 14:    // 'a' - AM_PM
                value = calendar.get(Calendar.AM_PM);
                current = formatData.getAmPmStrings()[value];
                break;
            case 15: // 'h' - HOUR:1-based.  eg, 11PM + 1 hour =>> 12 AM
                if ((value = calendar.get(Calendar.HOUR)) == 0)
                    current = zeroPaddingNumber(
                            calendar.getLeastMaximum(Calendar.HOUR) + 1,
                            count, maxIntCount, nf);
                else
                    current = zeroPaddingNumber(value, count, maxIntCount, nf);
                break;
            case 16: // 'K' - HOUR: 0-based.  eg, 11PM + 1 hour =>> 0 AM
                value = calendar.get(Calendar.HOUR);
                current = zeroPaddingNumber(value, count, maxIntCount, nf);
                break;
            case 17: // 'z' - ZONE_OFFSET
                int zoneIndex = getZoneIndex(calendar.getTimeZone().getID(), formatData);
                if (zoneIndex == -1) {
                    StringBuffer zoneString = new StringBuffer();
                    value = calendar.get(Calendar.ZONE_OFFSET)
                            + calendar.get(Calendar.DST_OFFSET);
                    if (value < 0) {
                        zoneString.append("GMT-");
                        value = -value; // suppress the '-' sign for text display.
                    } else
                        zoneString.append("GMT+");
                    zoneString.append(
                            zeroPaddingNumber((int) (value / (60 * 60 * 1000)), 2, 2, nf));
                    zoneString.append(':');
                    zoneString.append(
                            zeroPaddingNumber(
                                    (int) ((value % (60 * 60 * 1000)) / (60 * 1000)), 2, 2, nf));
                    current = zoneString.toString();
                } else if (calendar.get(Calendar.DST_OFFSET) != 0) {
                    if (count >= 4)
                        current = formatData.getZoneStrings()[zoneIndex][3];
                    else
                        // count < 4, use abbreviated form if exists
                        current = formatData.getZoneStrings()[zoneIndex][4];
                } else {
                    if (count >= 4)
                        current = formatData.getZoneStrings()[zoneIndex][1];
                    else
                        current = formatData.getZoneStrings()[zoneIndex][2];
                }
                break;
        }

        return current;
    }


    String zeroPaddingNumber(long value, int minDigits, int maxDigits,
                             NumberFormat nf) {
        nf.setMinimumIntegerDigits(minDigits);
        nf.setMaximumIntegerDigits(maxDigits);
        return nf.format(value);
    }


    int getZoneIndex(String ID, DateFormatSymbols dfs) {
        String[][] zoneStrings = dfs.getZoneStrings();

        for (int index = 0; index < zoneStrings.length; index++) {
            if (ID.equalsIgnoreCase(zoneStrings[index][0])) return index;
        }
        return -1;
    }


    final int second = 1000;
    final int minute = 60 * second;
    final int hour = 60 * minute;
    final int day = 24 * hour;
    final int month = 30 * day;
    final int year = 365 * day;
    final int someday = 30 * year + 3 * month + 19 * day + 5 * hour;


    /* standalone interface */
    public static void main(String argv[]) {
        Locale defaultLocale = Locale.getDefault();
        SDFTCKZoneNamesTest test = new SDFTCKZoneNamesTest();

        try {
            List.of(Locale.ROOT,
                    Locale.CHINA,
                    Locale.forLanguageTag("es-419"),
                    Locale.GERMANY,
                    Locale.forLanguageTag("hi-IN"),
                    Locale.JAPAN,
                    Locale.TAIWAN,
                    Locale.UK,
                    Locale.US,
                    Locale.forLanguageTag("uz-Cyrl-UZ"),
                    Locale.forLanguageTag("zh-SG"),
                    Locale.forLanguageTag("zh-HK"),
                    Locale.forLanguageTag("zh-MO")).stream()
                .forEach(l -> {
                    System.out.printf("Testing locale: %s%n", l);
                    Locale.setDefault(l);
                    test.SimpleDateFormat0062();
                });
        } finally {
            Locale.setDefault(defaultLocale);
        }
    }


    /**
     * Equivalence class partitioning
     * with state, input and output values orientation
     * for public StringBuffer format(Date date, StringBuffer result, FieldPosition fp),
     * <br><b>pre-conditions</b>: patterns: { "'s0mething'z mm::hh,yyyy zz",
     * "zzzz",
     * "z"} (each pattern contains letter for TIMEZONE_FIELD),
     * <br><b>date</b>: a Date object
     * <br><b>result</b>: a string
     * <br><b>fp</b>: a FieldPosition object with TIMEZONE_FIELD field
     * <br><b>output</b>: formatted date as expected.
     */
    public void SimpleDateFormat0062() {
        boolean passed = true;
        String patterns[] = {"'s0mething'z mm::hh,yyyy zz",
                "zzzz",
                "z"};
        SimpleDateFormat sdf = new SimpleDateFormat();
        Date date = new Date(1234567890);
        for (String[] tz : sdf.getDateFormatSymbols().getZoneStrings()) {
            sdf.setTimeZone(TimeZone.getTimeZone(tz[0]));
            for (int i = 0; i < patterns.length && passed; i++) {
                StringBuffer result = new StringBuffer("qwerty");
                FieldPosition fp = new FieldPosition(DateFormat.TIMEZONE_FIELD);
                sdf.applyPattern(patterns[i]);
                String expected = new
                        StringBuffer("qwerty").append(myFormat(date,
                        sdf)).toString();
                String formatted = sdf.format(date, result, fp).toString();

                if (!expected.equals(formatted)) {
                    System.out.println(
                            "method format(date, StringBuffer, FieldPosition) formats wrong");
                    System.out.println("  pattern: " + patterns[i]);
                    System.out.println("  time zone ID:   " + tz[0]);
                    System.out.println("  expected result:  " + expected);
                    System.out.println("  formatted result: " + formatted);
                    passed = false;
                }

                if (passed && !expected.equals(result.toString())) {
                    System.out.println(
                            "method format(Date date, StringBuffer toAppendTo, FieldPosition fp) toAppendTo is not " +
                                    "equal to output");
                    System.out.println("  pattern: " + patterns[i]);
                    System.out.println("  time zone ID:   " + tz[0]);
                    System.out.println("  toAppendTo   : " + result);
                    System.out.println("  formatted date: " + formatted);
                    passed = false;
                }
            }
        }
        if(passed)
        {
            System.out.println("PASSED : OKAY");
        }else
        {
            throw new RuntimeException("FAILED");
        }
    }
}