| /* | |
| * Copyright (c) 2001, 2023, 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 4322313 4833268 6302990 6304305 | |
| * @summary Make sure that new implementation for | |
| * SimpleDateFormat.parse('z' or 'Z') and format('z' or 'Z') work correctly. | |
| * @run junit Bug4322313 | |
| */ | |
| import java.io.*; | |
| import java.text.*; | |
| import java.util.*; | |
| import org.junit.jupiter.api.Test; | |
| import static org.junit.jupiter.api.Assertions.fail; | |
| public class Bug4322313 { | |
| @Test | |
| public void Test4322313() { | |
| Locale savedLocale = Locale.getDefault(); | |
| TimeZone savedTimeZone = TimeZone.getDefault(); | |
| boolean err = false; | |
| long mpm = 60 * 1000; /* Milliseconds per a minute */ | |
| Locale[] locs = {Locale.US, Locale.JAPAN, Locale.UK, Locale.of("ar")}; | |
| String[] formats = { | |
| "z", | |
| "Z", | |
| }; | |
| Object[][] valids = { | |
| /* given ID offset format('z'), ('Z') index */ | |
| {"GMT+03:04", -184L * mpm, "GMT+03:04", "+0304", 9}, | |
| {"GMT+13:42", -822L * mpm, "GMT+13:42", "+1342", 9}, | |
| {"GMT+00:00", 0L, "GMT+00:00", "+0000", 9}, | |
| {"GMT+1:11", -71L * mpm, "GMT+01:11", "+0111", 8}, | |
| {"GMT +13:42", 0L, "GMT", "+0000", 3}, | |
| {" GMT", 0L, "GMT", "+0000", 4}, | |
| {"+0304", -184L * mpm, "GMT+03:04", "+0304", 5}, | |
| {"+1342", -822L * mpm, "GMT+13:42", "+1342", 5}, | |
| {"+0000", 0L, "GMT+00:00", "+0000", 5}, | |
| {" +1342", -822L * mpm, "GMT+13:42", "+1342", 6}, | |
| /* ISO-LATIN-1 digits */ | |
| {"GMT+\u0030\u0031:\u0032\u0033", -83L * mpm, "GMT+01:23", "+0123", 9}, | |
| /* In fact, this test case is skipped because TimeZone class can't | |
| * recognize TimeZone IDs like "+00234" or "-00234". | |
| */ | |
| {"+00234", -23L * mpm, "GMT+00:23", "+0023", 5}, | |
| {"GMT-03:04", 184L * mpm, "GMT-03:04", "-0304", 9}, | |
| {"GMT-13:42", 822L * mpm, "GMT-13:42", "-1342", 9}, | |
| {"GMT-00:00", 0L, "GMT+00:00", "+0000", 9}, | |
| {"GMT-1:11", 71L * mpm, "GMT-01:11", "-0111", 8}, | |
| {"GMT -13:42", 0L, "GMT", "+0000", 3}, | |
| {"-0304", 184L * mpm, "GMT-03:04", "-0304", 5}, | |
| {"-1342", 822L * mpm, "GMT-13:42", "-1342", 5}, | |
| {" -1342", 822L * mpm, "GMT-13:42", "-1342", 6}, | |
| /* ISO-LATIN-1 digits */ | |
| {"GMT-\u0030\u0031:\u0032\u0033", 83L * mpm, "GMT-01:23", "-0123", 9}, | |
| /* In fact, this test case is skipped because TimeZone class can't | |
| * recognize TimeZone IDs like "+00234" or "-00234". | |
| */ | |
| {"-00234", 23L * mpm, "GMT+00:23", "-0023", 5}, | |
| }; | |
| Object[][] invalids = { | |
| /* given ID error index */ | |
| {"GMT+8", 5}, | |
| {"GMT+18", 6}, | |
| {"GMT+208", 6}, | |
| {"GMT+0304", 6}, | |
| {"GMT+42195", 5}, | |
| {"GMT+5:8", 7}, | |
| {"GMT+23:60", 8}, | |
| {"GMT+11:1", 8}, | |
| {"GMT+24:13", 5}, | |
| {"GMT+421:950", 5}, | |
| {"GMT+0a:0A", 5}, | |
| {"GMT+ 13:42", 4}, | |
| {"GMT+13 :42", 6}, | |
| {"GMT+13: 42", 7}, | |
| {"GMT+-13:42", 4}, | |
| {"G M T", 0}, | |
| {"+8", 2}, | |
| {"+18", 3}, | |
| {"+208", 4}, | |
| {"+2360", 4}, | |
| {"+2413", 2}, | |
| {"+42195", 2}, | |
| {"+0AbC", 2}, | |
| {"+ 1342", 1}, | |
| {"+-1342", 1}, | |
| {"1342", 0}, | |
| /* Arabic-Indic digits */ | |
| {"GMT+\u0660\u0661:\u0662\u0663", 4}, | |
| /* Extended Arabic-Indic digits */ | |
| {"GMT+\u06f0\u06f1:\u06f2\u06f3", 4}, | |
| /* Devanagari digits */ | |
| {"GMT+\u0966\u0967:\u0968\u0969", 4}, | |
| /* Fullwidth digits */ | |
| {"GMT+\uFF10\uFF11:\uFF12\uFF13", 4}, | |
| {"GMT-8", 5}, | |
| {"GMT-18", 6}, | |
| {"GMT-208", 6}, | |
| {"GMT-0304", 6}, | |
| {"GMT-42195", 5}, | |
| {"GMT-5:8", 7}, | |
| {"GMT-23:60", 8}, | |
| {"GMT-11:1", 8}, | |
| {"GMT-24:13", 5}, | |
| {"GMT-421:950", 5}, | |
| {"GMT-0a:0A", 5}, | |
| {"GMT- 13:42", 4}, | |
| {"GMT-13 :42", 6}, | |
| {"GMT-13: 42", 7}, | |
| {"GMT-+13:42", 4}, | |
| {"-8", 2}, | |
| {"-18", 3}, | |
| {"-208", 4}, | |
| {"-2360", 4}, | |
| {"-2413", 2}, | |
| {"-42195", 2}, | |
| {"-0AbC", 2}, | |
| {"- 1342", 1}, | |
| {"--1342", 1}, | |
| {"-802", 2}, | |
| /* Arabic-Indic digits */ | |
| {"GMT-\u0660\u0661:\u0662\u0663", 4}, | |
| /* Extended Arabic-Indic digits */ | |
| {"GMT-\u06f0\u06f1:\u06f2\u06f3", 4}, | |
| /* Devanagari digits */ | |
| {"GMT-\u0966\u0967:\u0968\u0969", 4}, | |
| /* Fullwidth digits */ | |
| {"GMT-\uFF10\uFF11:\uFF12\uFF13", 4}, | |
| }; | |
| try { | |
| for (int i=0; i < locs.length; i++) { | |
| Locale locale = locs[i]; | |
| Locale.setDefault(locale); | |
| for (int j=0; j < formats.length; j++) { | |
| TimeZone.setDefault(TimeZone.getTimeZone("GMT")); | |
| SimpleDateFormat sdf = new SimpleDateFormat(formats[j]); | |
| Date date; | |
| /* Okay case */ | |
| for (int k=0; k < valids.length; k++) { | |
| ParsePosition pos = new ParsePosition(0); | |
| try { | |
| date = sdf.parse((String)valids[k][0], pos); | |
| } | |
| catch (Exception e) { | |
| err = true; | |
| System.err.println("\tParse Error [Locale=" + | |
| locale + ", " + formats[j] + | |
| "/\"" + valids[k][0] + | |
| "\"] Unexpected Exception occurred: " + e); | |
| continue; | |
| } | |
| int offset = pos.getIndex(); | |
| if (offset != ((Integer)valids[k][4]).intValue()) { | |
| err = true; | |
| System.err.println("\tParse Error [Locale=" + | |
| locale + ", " + formats[j] + | |
| "/\"" + valids[k][0] + | |
| "\"] invalid index: expected:" + valids[k][4] + | |
| ", got:" + offset); | |
| } | |
| if (date.getTime() != ((Long)valids[k][1]).longValue()) { | |
| err = true; | |
| System.err.println("\tParse Error [Locale=" + | |
| locale + ", " + formats[j] + | |
| "/\"" + valids[k][0] + | |
| "\"] expected:" + valids[k][1] + | |
| ", got:" + date.getTime() + ", " + date); | |
| } else { | |
| /* | |
| System.out.println("\tParse Okay [Locale=" + | |
| locale) + ", " + formats[j] + | |
| "/\"" + valids[k][0] + | |
| "\"] expected:" + valids[k][1] + | |
| ", got:" + date.getTime() + ", " + date); | |
| */ | |
| try { | |
| date = sdf.parse((String)valids[k][0]); | |
| } | |
| catch (Exception e) { | |
| err = true; | |
| System.err.println("\tParse Error [Locale=" + | |
| locale + ", " + formats[j] + | |
| "/\"" + valids[k][0] + | |
| "\"] Unexpected Exception occurred: " + e); | |
| continue; | |
| } | |
| /* Since TimeZone.getTimeZone() don't treat | |
| * "+00234" or "-00234" as a valid ID, skips. | |
| */ | |
| if (((String)valids[k][0]).length() == 6) { | |
| continue; | |
| } | |
| /* Since TimeZone.getTimeZone() don't recognize | |
| * +hhmm/-hhmm format, add "GMT" as prefix. | |
| */ | |
| sdf.setTimeZone(TimeZone.getTimeZone( | |
| (((((String)valids[k][0]).charAt(0) != 'G') ? | |
| "GMT" : "") + valids[k][0]))); | |
| StringBuffer s = new StringBuffer(); | |
| sdf.format(date, s, new FieldPosition(0)); | |
| sdf.setTimeZone(TimeZone.getTimeZone("GMT")); | |
| String got = s.toString(); | |
| String expected = (String)valids[k][2+j]; | |
| if (!got.equals(expected) && | |
| // special case to allow the difference between | |
| // DateFormatSymbols.getZoneStrings() and | |
| // TimeZone.getDisplayName() for "GMT+-00:00" | |
| !(got.equals("GMT-00:00") && | |
| expected.equals("GMT+00:00"))) { | |
| err = true; | |
| System.err.println("\tFormat Error [Locale=" + | |
| locale + ", " + | |
| formats[j] + "/\"" + valids[k][0] + | |
| "\"] expected:" + valids[k][2+j] + | |
| ", got:" + s + ", " + date); | |
| } else { | |
| /* | |
| System.out.println("\tFormat Okay [Locale=" + | |
| locale + ", " + | |
| formats[j] + "/\"" + valids[k][0] + | |
| "\"] expected:" + valids[k][2+j] + | |
| ", got:" + s + ", " + date); | |
| */ | |
| } | |
| } | |
| } | |
| /* Error case 1 | |
| * using SimpleDateFormat.parse(String, ParsePosition) | |
| */ | |
| for (int k=0; k < invalids.length; k++) { | |
| ParsePosition pos = new ParsePosition(0); | |
| try { | |
| date = sdf.parse((String)invalids[k][0], pos); | |
| if (date != null) { | |
| err = true; | |
| System.err.println("\tParse Error [Locale=" + | |
| locale + ", " + formats[j] + | |
| "/\"" + invalids[k][0] + | |
| "\"] expected:null , got:" + date); | |
| } | |
| int offset = pos.getErrorIndex(); | |
| if (offset != ((Integer)invalids[k][1]).intValue()) { | |
| err = true; | |
| System.err.println("\tParse Error [Locale=" + | |
| locale + ", " + formats[j] + | |
| "/\"" + invalids[k][0] + | |
| "\"] incorrect offset. expected:" + | |
| invalids[k][1] + ", got: " + offset); | |
| } else { | |
| /* | |
| System.out.println("\tParse Okay [Locale=" + | |
| locale + ", " + formats[j] + | |
| "/\"" + invalids[k][0] + | |
| "\"] correct offset: " + offset); | |
| */ | |
| } | |
| } | |
| catch (Exception e) { | |
| err = true; | |
| System.err.println("\tParse Error [Locale=" + | |
| locale + ", " + formats[j] + | |
| "/\"" + invalids[k][0] + | |
| "\"] Unexpected Exception occurred: " + e); | |
| } | |
| } | |
| /* Error case 2 | |
| * using DateFormat.parse(String) | |
| */ | |
| boolean correctParseException = false; | |
| for (int k=0; k < invalids.length; k++) { | |
| try { | |
| date = sdf.parse((String)invalids[k][0]); | |
| } | |
| catch (ParseException e) { | |
| correctParseException = true; | |
| int offset = e.getErrorOffset(); | |
| if (offset != ((Integer)invalids[k][1]).intValue()) { | |
| err = true; | |
| System.err.println("\tParse Error [Locale=" + | |
| locale + ", " + formats[j] + | |
| "/\"" + invalids[k][0] + | |
| "\"] Expected exception occurred with an incorrect offset. expected:" + | |
| invalids[k][1] + ", got: " + offset); | |
| } else { | |
| /* | |
| System.out.println("\tParse Okay [Locale=" + | |
| locale + ", " + formats[j] + | |
| "/\"" + invalids[k][0] + | |
| "\"] Expected exception occurred with an correct offset: " | |
| + offset); | |
| */ | |
| } | |
| } | |
| catch (Exception e) { | |
| err = true; | |
| System.err.println("\tParse Error [Locale=" + | |
| locale + ", " + formats[j] + | |
| "/\"" + invalids[k][0] + | |
| "\"] Invalid exception occurred: " + e); | |
| } | |
| finally { | |
| if (!correctParseException) { | |
| err = true; | |
| System.err.println("\tParse Error: [Locale=" + | |
| locale + ", " + formats[j] + | |
| "/\"" + invalids[k][0] + | |
| "\"] Expected exception didn't occur."); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| finally { | |
| Locale.setDefault(savedLocale); | |
| TimeZone.setDefault(savedTimeZone); | |
| if (err) { | |
| fail("SimpleDateFormat.parse()/format() test failed"); | |
| } | |
| } | |
| } | |
| } |