1 /*
  2  * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 /**
 25  * @test
 26  * @bug 4322313 4833268 6302990 6304305
 27  * @library /java/text/testlib
 28  * @summary Make sure that new implementation for
 29  * SimpleDateFormat.parse('z' or 'Z') and format('z' or 'Z') work correctly.
 30  */
 31 
 32 import java.io.*;
 33 import java.text.*;
 34 import java.util.*;
 35 
 36 public class Bug4322313 extends IntlTest {
 37 
 38     public void Test4322313() {
 39         Locale savedLocale = Locale.getDefault();
 40         TimeZone savedTimeZone = TimeZone.getDefault();
 41         boolean err = false;
 42         long mpm = 60 * 1000;   /* Milliseconds per a minute */
 43 
 44         Locale[] locs = {Locale.US, Locale.JAPAN, Locale.UK, Locale.of("ar")};
 45 
 46         String[] formats = {
 47             "z",
 48             "Z",
 49         };
 50 
 51         Object[][] valids = {
 52           /* given ID      offset                format('z'), ('Z')    index */
 53             {"GMT+03:04",  -184L * mpm, "GMT+03:04", "+0304", 9},
 54             {"GMT+13:42",  -822L * mpm, "GMT+13:42", "+1342", 9},
 55             {"GMT+00:00",   0L,         "GMT+00:00", "+0000", 9},
 56             {"GMT+1:11",   -71L * mpm,  "GMT+01:11", "+0111", 8},
 57             {"GMT +13:42",  0L,         "GMT",       "+0000", 3},
 58             {" GMT",        0L,         "GMT",       "+0000", 4},
 59             {"+0304",      -184L * mpm, "GMT+03:04", "+0304", 5},
 60             {"+1342",      -822L * mpm, "GMT+13:42", "+1342", 5},
 61             {"+0000",       0L,         "GMT+00:00", "+0000", 5},
 62             {" +1342",     -822L * mpm, "GMT+13:42", "+1342", 6},
 63             /* ISO-LATIN-1 digits */
 64             {"GMT+\u0030\u0031:\u0032\u0033", -83L * mpm, "GMT+01:23", "+0123", 9},
 65 
 66            /* In fact, this test case is skipped because TimeZone class can't
 67             * recognize TimeZone IDs like "+00234" or "-00234".
 68             */
 69             {"+00234",     -23L * mpm, "GMT+00:23", "+0023", 5},
 70 
 71             {"GMT-03:04",  184L * mpm, "GMT-03:04", "-0304", 9},
 72             {"GMT-13:42",  822L * mpm, "GMT-13:42", "-1342", 9},
 73             {"GMT-00:00",  0L,         "GMT+00:00", "+0000", 9},
 74             {"GMT-1:11",   71L * mpm,  "GMT-01:11", "-0111", 8},
 75             {"GMT -13:42", 0L,         "GMT",       "+0000", 3},
 76             {"-0304",      184L * mpm, "GMT-03:04", "-0304", 5},
 77             {"-1342",      822L * mpm, "GMT-13:42", "-1342", 5},
 78             {" -1342",     822L * mpm, "GMT-13:42", "-1342", 6},
 79             /* ISO-LATIN-1 digits */
 80             {"GMT-\u0030\u0031:\u0032\u0033", 83L * mpm, "GMT-01:23", "-0123", 9},
 81            /* In fact, this test case is skipped because TimeZone class can't
 82             * recognize TimeZone IDs like "+00234" or "-00234".
 83             */
 84             {"-00234",     23L * mpm,  "GMT+00:23", "-0023", 5},
 85         };
 86 
 87         Object[][] invalids = {
 88           /* given ID       error index   */
 89             {"GMT+8",       5},
 90             {"GMT+18",      6},
 91             {"GMT+208",     6},
 92             {"GMT+0304",    6},
 93             {"GMT+42195",   5},
 94             {"GMT+5:8",     7},
 95             {"GMT+23:60",   8},
 96             {"GMT+11:1",    8},
 97             {"GMT+24:13",   5},
 98             {"GMT+421:950", 5},
 99             {"GMT+0a:0A",   5},
100             {"GMT+ 13:42",  4},
101             {"GMT+13 :42",  6},
102             {"GMT+13: 42",  7},
103             {"GMT+-13:42",  4},
104             {"G M T",       0},
105             {"+8",          2},
106             {"+18",         3},
107             {"+208",        4},
108             {"+2360",       4},
109             {"+2413",       2},
110             {"+42195",      2},
111             {"+0AbC",       2},
112             {"+ 1342",      1},
113             {"+-1342",      1},
114             {"1342",        0},
115           /* Arabic-Indic digits */
116             {"GMT+\u0660\u0661:\u0662\u0663", 4},
117           /* Extended Arabic-Indic digits */
118             {"GMT+\u06f0\u06f1:\u06f2\u06f3", 4},
119           /* Devanagari digits */
120             {"GMT+\u0966\u0967:\u0968\u0969", 4},
121           /* Fullwidth digits */
122             {"GMT+\uFF10\uFF11:\uFF12\uFF13", 4},
123 
124             {"GMT-8",       5},
125             {"GMT-18",      6},
126             {"GMT-208",     6},
127             {"GMT-0304",    6},
128             {"GMT-42195",   5},
129             {"GMT-5:8",     7},
130             {"GMT-23:60",   8},
131             {"GMT-11:1",    8},
132             {"GMT-24:13",   5},
133             {"GMT-421:950", 5},
134             {"GMT-0a:0A",   5},
135             {"GMT- 13:42",  4},
136             {"GMT-13 :42",  6},
137             {"GMT-13: 42",  7},
138             {"GMT-+13:42",  4},
139             {"-8",          2},
140             {"-18",         3},
141             {"-208",        4},
142             {"-2360",       4},
143             {"-2413",       2},
144             {"-42195",      2},
145             {"-0AbC",       2},
146             {"- 1342",      1},
147             {"--1342",      1},
148             {"-802",        2},
149           /* Arabic-Indic digits */
150             {"GMT-\u0660\u0661:\u0662\u0663", 4},
151           /* Extended Arabic-Indic digits */
152             {"GMT-\u06f0\u06f1:\u06f2\u06f3", 4},
153           /* Devanagari digits */
154             {"GMT-\u0966\u0967:\u0968\u0969", 4},
155           /* Fullwidth digits */
156             {"GMT-\uFF10\uFF11:\uFF12\uFF13", 4},
157         };
158 
159         try {
160             for (int i=0; i < locs.length; i++) {
161                 Locale locale = locs[i];
162                 Locale.setDefault(locale);
163 
164                 for (int j=0; j < formats.length; j++) {
165                     TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
166                     SimpleDateFormat sdf = new SimpleDateFormat(formats[j]);
167                     Date date;
168 
169                     /* Okay case */
170                     for (int k=0; k < valids.length; k++) {
171                         ParsePosition pos = new ParsePosition(0);
172                         try {
173                             date = sdf.parse((String)valids[k][0], pos);
174                         }
175                         catch (Exception e) {
176                             err = true;
177                             System.err.println("\tParse  Error [Locale=" +
178                                 locale + ", " + formats[j] +
179                                 "/\"" + valids[k][0] +
180                                 "\"] Unexpected Exception occurred: " + e);
181                             continue;
182                         }
183 
184                         int offset = pos.getIndex();
185                         if (offset != ((Integer)valids[k][4]).intValue()) {
186                             err = true;
187                             System.err.println("\tParse  Error [Locale=" +
188                                 locale + ", " + formats[j] +
189                                 "/\"" + valids[k][0] +
190                                 "\"] invalid index: expected:" + valids[k][4] +
191                                 ", got:" + offset);
192                         }
193 
194                         if (date.getTime() != ((Long)valids[k][1]).longValue()) {
195                             err = true;
196                             System.err.println("\tParse  Error [Locale=" +
197                                 locale + ", " + formats[j] +
198                                 "/\"" + valids[k][0] +
199                                 "\"] expected:" + valids[k][1] +
200                                 ", got:" + date.getTime() + ", " + date);
201                         } else {
202 /*
203                             logln("\tParse  Okay  [Locale=" +
204                                 locale) + ", " + formats[j] +
205                                 "/\"" + valids[k][0] +
206                                 "\"] expected:" + valids[k][1] +
207                                 ", got:" + date.getTime() + ", " + date);
208 */
209 
210                             try {
211                                 date = sdf.parse((String)valids[k][0]);
212                             }
213                             catch (Exception e) {
214                                 err = true;
215                                 System.err.println("\tParse  Error [Locale=" +
216                                     locale + ", " + formats[j] +
217                                     "/\"" + valids[k][0] +
218                                     "\"] Unexpected Exception occurred: " + e);
219                                 continue;
220                             }
221 
222                             /* Since TimeZone.getTimeZone() don't treat
223                              * "+00234" or "-00234" as a valid ID, skips.
224                              */
225                             if (((String)valids[k][0]).length() == 6) {
226                                 continue;
227                             }
228 
229                             /* Since TimeZone.getTimeZone() don't recognize
230                              * +hhmm/-hhmm format, add "GMT" as prefix.
231                              */
232                             sdf.setTimeZone(TimeZone.getTimeZone(
233                                 (((((String)valids[k][0]).charAt(0) != 'G') ?
234                                 "GMT" : "") + valids[k][0])));
235                             StringBuffer s = new StringBuffer();
236                             sdf.format(date, s, new FieldPosition(0));
237                             sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
238 
239                             String got = s.toString();
240                             String expected = (String)valids[k][2+j];
241                             if (!got.equals(expected) &&
242                                 // special case to allow the difference between
243                                 // DateFormatSymbols.getZoneStrings() and
244                                 // TimeZone.getDisplayName() for "GMT+-00:00"
245                                 !(got.equals("GMT-00:00") &&
246                                   expected.equals("GMT+00:00"))) {
247                                 err = true;
248                                 System.err.println("\tFormat Error [Locale=" +
249                                     locale + ", " +
250                                     formats[j] + "/\"" + valids[k][0] +
251                                     "\"] expected:" + valids[k][2+j] +
252                                     ", got:" + s + ", " + date);
253                             } else {
254 /*
255                                 logln("\tFormat Okay  [Locale=" +
256                                     locale + ", " +
257                                     formats[j] + "/\"" + valids[k][0] +
258                                     "\"] expected:" + valids[k][2+j] +
259                                     ", got:" + s + ", " + date);
260 */
261                             }
262                         }
263                     }
264 
265                     /* Error case 1
266                      *   using SimpleDateFormat.parse(String, ParsePosition)
267                      */
268                     for (int k=0; k < invalids.length; k++) {
269                         ParsePosition pos = new ParsePosition(0);
270                         try {
271                             date = sdf.parse((String)invalids[k][0], pos);
272                             if (date != null) {
273                                 err = true;
274                                 System.err.println("\tParse  Error [Locale=" +
275                                     locale + ", " + formats[j] +
276                                     "/\"" + invalids[k][0] +
277                                     "\"] expected:null , got:" + date);
278                             }
279                             int offset = pos.getErrorIndex();
280                             if (offset != ((Integer)invalids[k][1]).intValue()) {
281                                 err = true;
282                                 System.err.println("\tParse  Error [Locale=" +
283                                     locale + ", " + formats[j] +
284                                     "/\"" + invalids[k][0] +
285                                     "\"] incorrect offset. expected:" +
286                                     invalids[k][1] + ", got: " + offset);
287                             } else {
288 /*
289                                 logln("\tParse  Okay  [Locale=" +
290                                     locale + ", " + formats[j] +
291                                     "/\"" + invalids[k][0] +
292                                     "\"] correct offset: " + offset);
293 */
294                             }
295                         }
296                         catch (Exception e) {
297                             err = true;
298                             System.err.println("\tParse  Error [Locale=" +
299                                 locale + ", " + formats[j] +
300                                 "/\"" + invalids[k][0] +
301                                 "\"] Unexpected Exception occurred: " + e);
302                         }
303                     }
304 
305                     /* Error case 2
306                      *   using DateFormat.parse(String)
307                      */
308                     boolean correctParseException = false;
309                     for (int k=0; k < invalids.length; k++) {
310                         try {
311                             date = sdf.parse((String)invalids[k][0]);
312                         }
313                         catch (ParseException e) {
314                             correctParseException = true;
315                             int offset = e.getErrorOffset();
316                             if (offset != ((Integer)invalids[k][1]).intValue()) {
317                                 err = true;
318                                 System.err.println("\tParse  Error [Locale=" +
319                                     locale + ", " + formats[j] +
320                                     "/\"" + invalids[k][0] +
321                                     "\"] Expected exception occurred with an incorrect offset. expected:" +
322                                     invalids[k][1] + ", got: " + offset);
323                             } else {
324 /*
325                                 logln("\tParse  Okay  [Locale=" +
326                                     locale + ", " + formats[j] +
327                                     "/\"" + invalids[k][0] +
328                                     "\"] Expected exception occurred with an correct offset: "
329                                     + offset);
330 */
331                             }
332                         }
333                         catch (Exception e) {
334                             err = true;
335                             System.err.println("\tParse  Error [Locale=" +
336                                 locale + ", " + formats[j] +
337                                 "/\"" + invalids[k][0] +
338                                 "\"] Invalid exception occurred: " + e);
339                         }
340                         finally {
341                             if (!correctParseException) {
342                                 err = true;
343                                 System.err.println("\tParse  Error: [Locale=" +
344                                     locale + ", " + formats[j] +
345                                     "/\"" + invalids[k][0] +
346                                     "\"] Expected exception didn't occur.");
347                             }
348                         }
349                     }
350                 }
351             }
352         }
353         finally {
354             Locale.setDefault(savedLocale);
355             TimeZone.setDefault(savedTimeZone);
356             if (err) {
357                 errln("SimpleDateFormat.parse()/format() test failed");
358             }
359         }
360     }
361 
362     public static void main(String[] args) throws Exception {
363         new Bug4322313().run(args);
364     }
365 }