1 /*
2  * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /**
25  * @test
26  * @bug 4823811 8008577
27  * @summary Confirm that text which includes numbers with a trailing minus sign is parsed correctly.
28  * @modules jdk.localedata
29  * @run main/othervm -Duser.timezone=GMT+09:00 -Djava.locale.providers=JRE,SPI Bug4823811
30  */
31 
32 import java.text.*;
33 import java.util.*;
34 
35 public class Bug4823811 {
36 
37     private static Locale localeEG = new Locale("ar", "EG");
main(String[] args)38     private static Locale localeUS = Locale.US;
39 
40     private static String JuneInArabic = "\u064a\u0648\u0646\u064a\u0648";
41     private static String JulyInArabic = "\u064a\u0648\u0644\u064a\u0648";
42     private static String JuneInEnglish = "June";
43     private static String JulyInEnglish = "July";
44 
45     private static String BORDER =
46         "============================================================";
47 
48     /*
49      * I don't use static import here intentionally so that this test program
50      * can be run on JDK 1.4.2.
51      */
52     private static int ERA = Calendar.ERA;
53     private static int BC = GregorianCalendar.BC;
54 //    private static int JAN = Calendar.JANUARY;
55 //    private static int FEB = Calendar.FEBRUARY;
56 //    private static int MAR = Calendar.MARCH;
57     private static int APR = Calendar.APRIL;
58     private static int MAY = Calendar.MAY;
59     private static int JUN = Calendar.JUNE;
60     private static int JUL = Calendar.JULY;
61 //    private static int AUG = Calendar.AUGUST;
62 //    private static int SEP = Calendar.SEPTEMBER;
63 //    private static int OCT = Calendar.OCTOBER;
64 //    private static int NOV = Calendar.NOVEMBER;
65 //    private static int DEC = Calendar.DECEMBER;
66 
67     private static String[] patterns = {
68         "yyyy MMMM d H m s",
69         "yyyy MM dd hh mm ss",
70 
71         /*
72          * Because 1-based HOUR_OF_DAY, 1-based HOUR, MONTH, and YEAR fields
73          * are parsed using different code from the code for other numeric
74          * fields, I prepared YEAR-preceding patterns and SECOND-preceding
75          * patterns.
76          */
77         "yyyy M d h m s",
78         " yyyy M d h m s",
79         "yyyy M d h m s ",
80 
81         "s m h d M yyyy",
82         " s m h d M yyyy",
83         "s m h d M yyyy ",
84     };
85 
86     private static char originalMinusSign1 = ':';
87     private static char originalMinusSign2 = '\uff0d';  // fullwidth minus
88     private static String[] delimiters = {"-", "/", ":", "/", "\uff0d", "/"};
89     private static String[][] specialDelimiters = {
90         // for Arabic formatter and modified English formatter
91         {"--", "-/", "::", ":/", "\uff0d\uff0d", "\uff0d/"},
92 
93         // for English formatter and modified Arabic formatter
94         {"--", "/-", "::", "/:", "\uff0d\uff0d", "/\uff0d"},
95     };
96 
97     /*
98      * Format:
99      *   +-------------------------------------------------------------------+
100      *   | Input               | Output                                      |
101      *   +---------------------+---------------------------------------------|
102      *   | datesEG & datesUS   | formattedDatesEG & formattedDatesUS         |
103      *   +-------------------------------------------------------------------+
104      *
105      * Parse:
106      *   +-------------------------------------------------------------------+
107      *   | Input               | Output                                      |
108      *   |---------------------+---------------------------------------------|
109      *   | datesToParse        | datesEG & datesUS                           |
110      *   +-------------------------------------------------------------------+
111      */
112     private static String[][] datesToParse = {
113         // "JUNE" and "JULY" are replaced with a localized month name later.
114         {"2008 JULY 20 3 12 83",
115          "2008  JULY 20 3 12 83",
116          "2008 JULY  20 3 12 83"},
117 
118         {"2008 07 20 03 12 83",
119          "2008  07 20 03 12 83",
120          "2008 07  20 03 12 83"},
121 
122         {"2008 7 20 3 12 83",
123          "2008  7 20  3 12 83",
124          "2008 7  20  3 12 83"},
125 
126         {" 2008 7 20 3 12 83",
127          "  2008 7 20 3 12 83",
128          " 2008  7 20 3 12 83",
129          "2008 7 20 3 12 83"},
130 
131         {"2008 7 20 3 12 83 ",
132          "2008 7 20 3 12 83  ",
133          "2008 7 20 3 12 83"},
134 
135         {"83 12 3 20 7 2008",
136          "83  12 3  20 7 2008",
137          "83 12  3  20 7 2008"},
138 
139         {" 83 12 3 20 7 2008",
140          "  83 12 3 20 7 2008",
141          " 83  12 3 20 7 2008",
142          "83 12 3 20 7 2008"},
143 
144         {"83 12 3 20 7 2008 ",
145          "83 12 3 20 7 2008  ",
146          "83 12 3 20 7 2008"},
147     };
148 
149     // For formatting
150     private static String[][] formattedDatesEG = {
151         {"2008 JULY 20 3 13 23",
152          "2009 JULY 20 3 13 23",
153          null},
154 
155         {"2008 07 20 03 13 23",
156          "2009 07 20 03 13 23",
157          "2007 05 20 03 13 23"},
158 
159         {"2008 7 20 3 13 23",
160          "2009 6 10 3 13 23",
161          "2007 4 10 3 13 23"},
162 
163         {" 2008 7 20 3 13 23",
164          null,
165          " 2009 7 20 3 13 23",
166          null},
167 
168         {"2008 7 20 3 13 23 ",
169          "2008 7 20 3 10 37 ",
170          null},
171 
172         {"23 13 3 20 7 2008",
173          "37 10 9 19 7 2008",
174          "23 49 8 19 7 2008"},
175 
176         {" 23 13 3 20 7 2008",
177          null,
178          " 37 10 3 20 7 2008",
179          null},
180 
181         {"23 13 3 20 7 2008 ",
182          "23 13 3 20 7 2009 ",
183          null},
184     };
185 
186     private static String[][] formattedDatesUS = {
187         {"2008 JULY 20 3 13 23",
188          null,
189          "2008 JUNE 10 3 13 23"},
190 
191         {"2008 07 20 03 13 23",
192          "2007 05 20 03 13 23",
193          "2008 06 10 03 13 23"},
194 
195         {"2008 7 20 3 13 23",
196          "2007 5 19 9 13 23",
197          "2008 6 9 9 13 23"},
198 
199         {" 2008 7 20 3 13 23",
200          " 2009 7 20 3 13 23",
201          " 2007 5 20 3 13 23",
202          null},
203 
204         {"2008 7 20 3 13 23 ",
205          "2008 7 20 3 13 23 ",
206          null},
207 
208         {"23 13 3 20 7 2008",
209          "23 49 2 10 6 2008",
210          "23 13 9 9 6 2008"},
211 
212         {" 23 13 3 20 7 2008",
213          " 37 10 3 20 7 2008",
214          " 23 49 2 20 7 2008",
215          null},
216 
217         {"23 13 3 20 7 2008 ",
218          "23 13 3 20 7 2008 ",
219          null},
220     };
221 
222     private static GregorianCalendar[][] datesEG = {
223         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
224          new GregorianCalendar(-2008, JUL,  20,  3,  12,  83),
225          null},
226 
227         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
228          new GregorianCalendar(-2008, JUL,  20,  3,  12,  83),
229          new GregorianCalendar( 2007, MAY,  20,  3,  12,  83)},
230 
231         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
232          new GregorianCalendar(-2008, JUL, -20,  3,  12,  83),
233          new GregorianCalendar( 2007, APR,  10,  3,  12,  83)},
234 
235         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
236          null,
237          new GregorianCalendar(-2008, JUL,  20,  3,  12,  83),
238          null},
239 
240         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
241          new GregorianCalendar( 2008, JUL,  20,  3,  12, -83),
242          null},
243 
244         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
245          new GregorianCalendar( 2008, JUL,  20, -3,  12, -83),
246          new GregorianCalendar( 2008, JUL,  20, -3, -12,  83)},
247 
248         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
249          null,
250          new GregorianCalendar( 2008, JUL,  20,  3,  12, -83),
251          null},
252 
253         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
254          new GregorianCalendar(-2008, JUL,  20,  3,  12,  83),
255          null},
256     };
257 
258     private static GregorianCalendar[][] datesUS = {
259         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
260          null,
261          new GregorianCalendar( 2008, JUN,  10,  3,  12,  83)},
262 
263         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
264          new GregorianCalendar( 2007, MAY,  20,  3,  12,  83),
265          new GregorianCalendar( 2008, JUN,  10,  3,  12,  83)},
266 
267         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
268          new GregorianCalendar( 2007, MAY,  20, -3,  12,  83),
269          new GregorianCalendar( 2008, JUL, -20, -3,  12,  83)},
270 
271         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
272          new GregorianCalendar(-2008, JUL,  20,  3,  12,  83),
273          new GregorianCalendar( 2007, MAY,  20,  3,  12,  83),
274          null},
275 
276         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
277          new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
278          null},
279 
280         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
281          new GregorianCalendar( 2008, JUL, -20,  3, -12,  83),
282          new GregorianCalendar( 2008, JUL, -20, -3,  12,  83)},
283 
284         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
285          new GregorianCalendar( 2008, JUL,  20,  3,  12, -83),
286          new GregorianCalendar( 2008, JUL,  20,  3, -12,  83),
287          null},
288 
289         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
290          new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
291          null},
292     };
293 
294     /* flags */
295     private static boolean err = false;
296     private static boolean verbose = false;
297 
298 
299     public static void main(String[] args) {
300         if (args.length == 1 && args[0].equals("-v")) {
301             verbose = true;
302         }
303 
304         Locale defaultLocale = Locale.getDefault();
305         TimeZone defaultTimeZone = TimeZone.getDefault();
306 
307         TimeZone.setDefault(TimeZone.getTimeZone("Asia/Tokyo"));
308 
309         try {
310             /*
311              * Test SimpleDateFormat.parse() and format() for original
312              * SimpleDateFormat instances
313              */
314             testDateFormat1();
315 
316             /*
317              * Test SimpleDateFormat.parse() and format() for modified
318              * SimpleDateFormat instances using an original minus sign,
319              * pattern, and diffenrent month names in DecimalFormat
320              */
321             testDateFormat2();
322 
323             /*
324              * Test SimpleDateFormat.parse() and format() for modified
325              * SimpleDateFormat instances using a fullwidth minus sign
326              */
327             testDateFormat3();
328 
329             /*
330              * Just to confirm that regressions aren't introduced in
331              * DecimalFormat. This cannot happen, though. Because I didn't
332              * change DecimalFormat at all.
333              */
334             testNumberFormat();
335         }
336         catch (Exception e) {
337             err = true;
338             System.err.println("Unexpected exception: " + e);
339         }
340         finally {
341             Locale.setDefault(defaultLocale);
342             TimeZone.setDefault(defaultTimeZone);
343 
344             if (err) {
345                 System.err.println(BORDER + " Test failed.");
346                 throw new RuntimeException("Date/Number formatting/parsing error.");
347             } else {
348                 System.out.println(BORDER + " Test passed.");
349             }
350         }
351     }
352 
353 
354     //
355     // DateFormat test
356     //
357     private static void testDateFormat1() {
358         for (int i = 0; i < patterns.length; i++) {
359             System.out.println(BORDER);
360             for (int j = 0; j <= 1; j++) {
361                 // Generate a pattern
362                 String pattern = patterns[i].replaceAll(" ", delimiters[j]);
363                 System.out.println("Pattern=\"" + pattern + "\"");
364 
365                 System.out.println("*** DateFormat.format test in ar_EG");
366                 testDateFormatFormattingInRTL(pattern, i, j, null, localeEG, false);
367 
368                 System.out.println("*** DateFormat.parse test in ar_EG");
369                 testDateFormatParsingInRTL(pattern, i, j, null, localeEG, false);
370 
371                 System.out.println("*** DateFormat.format test in en_US");
372                 testDateFormatFormattingInLTR(pattern, i, j, null, localeUS, true);
373 
374                 System.out.println("*** DateFormat.parse test in en_US");
375                 testDateFormatParsingInLTR(pattern, i, j, null, localeUS, true);
376             }
377         }
378     }
379 
380     private static void testDateFormat2() {
381         /*
382          * modified ar_EG Date&Time formatter :
383          *   minus sign:  ':'
384          *   pattern:     "#,##0.###"
385          *   month names: In Arabic
386          *
387          * modified en_US Date&Time formatter :
388          *   minus sign:  ':'
389          *   pattern:     "#,##0.###;#,##0.###-"
390          *   month names: In English
391          */
392         DecimalFormat dfEG = (DecimalFormat)NumberFormat.getInstance(localeEG);
393         DecimalFormat dfUS = (DecimalFormat)NumberFormat.getInstance(localeUS);
394 
395         DecimalFormatSymbols dfsEG = dfEG.getDecimalFormatSymbols();
396         DecimalFormatSymbols dfsUS = dfUS.getDecimalFormatSymbols();
397         dfsEG.setMinusSign(originalMinusSign1);
398         dfsUS.setMinusSign(originalMinusSign1);
399         dfEG.setDecimalFormatSymbols(dfsUS);
400         dfUS.setDecimalFormatSymbols(dfsEG);
401 
402         String patternEG = dfEG.toPattern();
403         String patternUS = dfUS.toPattern();
404 
405         dfEG.applyPattern(patternUS);
406         dfUS.applyPattern(patternEG);
407 
408         for (int i = 0; i < patterns.length; i++) {
409             System.out.println(BORDER);
410             for (int j = 2; j <= 3; j++) {
411                 // Generate a pattern
412                 String pattern = patterns[i].replaceAll(" ", delimiters[j]);
413                 System.out.println("Pattern=\"" + pattern + "\"");
414 
415                 System.out.println("*** DateFormat.format test in modified en_US");
416                 testDateFormatFormattingInRTL(pattern, i, j, dfUS, localeUS, true);
417 
418                 System.out.println("*** DateFormat.parse test in modified en_US");
419                 testDateFormatParsingInRTL(pattern, i, j, dfUS, localeUS, true);
420 
421                 System.out.println("*** DateFormat.format test in modified ar_EG");
422                 testDateFormatFormattingInLTR(pattern, i, j, dfEG, localeEG, false);
423 
424                 System.out.println("*** DateFormat.parse test in modified ar_EG");
425                 testDateFormatParsingInLTR(pattern, i, j, dfEG, localeEG, false);
426             }
427         }
428     }
429 
430     private static void testDateFormat3() {
431         /*
432          * modified ar_EG Date&Time formatter :
433          *   minus sign:  '\uff0d'  // fullwidth minus
434          *   pattern:     "#,##0.###;#,##0.###-"
435          *   month names: In Arabic
436          *
437          * modified en_US Date&Time formatter :
438          *   minus sign:  '\uff0d'  // fullwidth minus
439          *   pattern:     "#,##0.###"
440          *   month names: In English
441          */
442         DecimalFormat dfEG = (DecimalFormat)NumberFormat.getInstance(localeEG);
443         DecimalFormat dfUS = (DecimalFormat)NumberFormat.getInstance(localeUS);
444 
445         DecimalFormatSymbols dfsEG = dfEG.getDecimalFormatSymbols();
446         DecimalFormatSymbols dfsUS = dfUS.getDecimalFormatSymbols();
447         dfsEG.setMinusSign(originalMinusSign2);
448         dfsUS.setMinusSign(originalMinusSign2);
449         dfEG.setDecimalFormatSymbols(dfsEG);
450         dfUS.setDecimalFormatSymbols(dfsUS);
451 
452         for (int i = 0; i < patterns.length; i++) {
453             System.out.println(BORDER);
454             for (int j = 4; j <= 5; j++) {
455                 // Generate a pattern
456                 String pattern = patterns[i].replaceAll(" ", delimiters[j]);
457                 System.out.println("Pattern=\"" + pattern + "\"");
458 
459                 System.out.println("*** DateFormat.format test in modified ar_EG");
460                 testDateFormatFormattingInRTL(pattern, i, j, dfEG, localeEG, false);
461 
462                 System.out.println("*** DateFormat.parse test in modified ar_EG");
463                 testDateFormatParsingInRTL(pattern, i, j, dfEG, localeEG, false);
464 
465                 System.out.println("*** DateFormat.format test in modified en_US");
466                 testDateFormatFormattingInLTR(pattern, i, j, dfUS, localeUS, true);
467 
468                 System.out.println("*** DateFormat.parse test in modified en_US");
469                 testDateFormatParsingInLTR(pattern, i, j, dfUS, localeUS, true);
470             }
471         }
472     }
473 
474     private static void testDateFormatFormattingInRTL(String pattern,
475                                                       int basePattern,
476                                                       int delimiter,
477                                                       NumberFormat nf,
478                                                       Locale locale,
479                                                       boolean useEnglishMonthName) {
480         Locale.setDefault(locale);
481 
482         SimpleDateFormat sdf = new SimpleDateFormat(pattern);
483         if (nf != null) {
484             sdf.setNumberFormat(nf);
485         }
486         for (int i = 0; i < datesToParse[basePattern].length; i++) {
487             if (datesEG[basePattern][i] == null) {
488                 continue;
489             }
490 
491             String expected = formattedDatesEG[basePattern][i]
492                               .replaceAll("JUNE", (useEnglishMonthName ?
493                                                    JuneInEnglish : JuneInArabic))
494                               .replaceAll("JULY", (useEnglishMonthName ?
495                                                    JulyInEnglish : JulyInArabic))
496                               .replaceAll(" ", delimiters[delimiter]);
497             testDateFormatFormatting(sdf, pattern, datesEG[basePattern][i],
498                 expected, locale.toString());
499         }
500     }
501 
502     private static void testDateFormatFormattingInLTR(String pattern,
503                                                       int basePattern,
504                                                       int delimiter,
505                                                       NumberFormat nf,
506                                                       Locale locale,
507                                                       boolean useEnglishMonthName) {
508         Locale.setDefault(locale);
509 
510         SimpleDateFormat sdf = new SimpleDateFormat(pattern);
511         if (nf != null) {
512             sdf.setNumberFormat(nf);
513         }
514         for (int i = 0; i < datesToParse[basePattern].length; i++) {
515             if (datesUS[basePattern][i] == null) {
516                 continue;
517             }
518 
519             String expected = formattedDatesUS[basePattern][i]
520                               .replaceAll("JUNE", (useEnglishMonthName ?
521                                                    JuneInEnglish : JuneInArabic))
522                               .replaceAll("JULY", (useEnglishMonthName ?
523                                                    JulyInEnglish : JulyInArabic))
524                               .replaceAll(" ", delimiters[delimiter]);
525             testDateFormatFormatting(sdf, pattern, datesUS[basePattern][i],
526                 expected, locale.toString());
527         }
528     }
529 
530     private static void testDateFormatFormatting(SimpleDateFormat sdf,
531                                                  String pattern,
532                                                  GregorianCalendar givenGC,
533                                                  String expected,
534                                                  String locale) {
535         Date given = givenGC.getTime();
536         String str = sdf.format(given);
537         if (expected.equals(str)) {
538             if (verbose) {
539                 System.out.print("  Passed: SimpleDateFormat(");
540                 System.out.print(locale + ", \"" + pattern + "\").format(");
541                 System.out.println(given + ")");
542 
543                 System.out.print("      ---> \"" + str + "\" ");
544                 System.out.println((givenGC.get(ERA) == BC) ? "(BC)" : "(AD)");
545             }
546         } else {
547             err = true;
548 
549             System.err.print("  Failed: Unexpected SimpleDateFormat(");
550             System.out.print(locale + ", \"" + pattern + "\").format(");
551             System.out.println(given + ") result.");
552 
553             System.out.println("      Expected: \"" + expected + "\"");
554 
555             System.out.print("      Got:      \"" + str + "\" ");
556             System.out.println((givenGC.get(ERA) == BC) ? "(BC)" : "(AD)");
557         }
558     }
559 
560     private static void testDateFormatParsingInRTL(String pattern,
561                                                    int basePattern,
562                                                    int delimiter,
563                                                    NumberFormat nf,
564                                                    Locale locale,
565                                                    boolean useEnglishMonthName) {
566         Locale.setDefault(locale);
567 
568         SimpleDateFormat sdf = new SimpleDateFormat(pattern);
569         if (nf != null) {
570             sdf.setNumberFormat(nf);
571         }
572         for (int i = 0; i < datesToParse[basePattern].length; i++) {
573             String given = datesToParse[basePattern][i]
574                            .replaceAll("  ", specialDelimiters[0][delimiter])
575                            .replaceAll(" ", delimiters[delimiter]);
576 
577             testDateFormatParsing(sdf, pattern,
578                 given.replaceAll("JULY", (useEnglishMonthName ?
579                                           JulyInEnglish :  JulyInArabic)),
580                 datesEG[basePattern][i], locale.toString());
581         }
582     }
583 
584     private static void testDateFormatParsingInLTR(String pattern,
585                                                    int basePattern,
586                                                    int delimiter,
587                                                    NumberFormat nf,
588                                                    Locale locale,
589                                                    boolean useEnglishMonthName) {
590         Locale.setDefault(locale);
591 
592         SimpleDateFormat sdf = new SimpleDateFormat(pattern);
593         if (nf != null) {
594             sdf.setNumberFormat(nf);
595         }
596         for (int i = 0; i < datesToParse[basePattern].length; i++) {
597             String given = datesToParse[basePattern][i]
598                            .replaceAll("  ", specialDelimiters[1][delimiter])
599                            .replaceAll(" ", delimiters[delimiter]);
600 
601             testDateFormatParsing(sdf, pattern,
602                 given.replaceAll("JULY", (useEnglishMonthName ?
603                                           JulyInEnglish :  JulyInArabic)),
604                 datesUS[basePattern][i], locale.toString());
605         }
606     }
607 
608     private static void testDateFormatParsing(SimpleDateFormat sdf,
609                                               String pattern,
610                                               String given,
611                                               GregorianCalendar expectedGC,
612                                               String locale) {
613         try {
614             Date d = sdf.parse(given);
615             if (expectedGC == null) {
616                 err = true;
617                 System.err.print("  Failed: SimpleDateFormat(" + locale);
618                 System.err.print(", \"" + pattern + "\").parse(\"" + given);
619                 System.err.println("\") should have thrown ParseException");
620             } else if (expectedGC.getTime().equals(d)) {
621                 if (verbose) {
622                     System.out.print("  Passed: SimpleDateFormat(" + locale);
623                     System.out.print(", \"" + pattern + "\").parse(\"" + given);
624                     System.out.println("\")");
625 
626                     System.out.print("      ---> " + d + " (" + d.getTime());
627                     System.out.println(")");
628                 }
629             } else {
630                 err = true;
631                 System.err.print("  Failed: SimpleDateFormat(" + locale);
632                 System.err.print(", \"" + pattern + "\").parse(\"" + given);
633                 System.err.println("\")");
634 
635                 System.err.print("      Expected: " + expectedGC.getTime());
636                 System.err.println(" (" + d.getTime() + ")");
637 
638                 System.err.print("      Got:      " + d + " (" + d.getTime());
639                 System.err.println(")");
640 
641                 System.err.print("      Pattern:  \"");
642                 System.err.print(((DecimalFormat)sdf.getNumberFormat()).toPattern());
643                 System.err.println("\"");
644             }
645         }
646         catch (ParseException pe) {
647             if (expectedGC == null) {
648                 if (verbose) {
649                     System.out.print("  Passed: SimpleDateFormat(" + locale);
650                     System.out.print(", \"" + pattern + "\").parse(\"" + given);
651                     System.out.println("\")");
652 
653                     System.out.println("      threw ParseException as expected");
654                 }
655             } else {
656                 err = true;
657                 System.err.println("  Failed: Unexpected exception with");
658 
659                 System.err.print("    SimpleDateFormat(" + locale);
660                 System.err.print(", \"" + pattern + "\").parse(\"");
661                 System.err.println(given + "\"):");
662 
663                 System.err.println("      " + pe);
664 
665                 System.err.print("      Pattern: \"");
666                 System.err.print(((DecimalFormat)sdf.getNumberFormat()).toPattern());
667                 System.err.println("\"");
668 
669                 System.err.print("      Month 0: ");
670                 System.err.println(sdf.getDateFormatSymbols().getMonths()[0]);
671             }
672         }
673     }
674 
675 
676     //
677     // NumberFormat test
678     //
679     private static void testNumberFormat() {
680         NumberFormat nfEG = NumberFormat.getInstance(localeEG);
681         NumberFormat nfUS = NumberFormat.getInstance(localeUS);
682 
683         System.out.println("*** DecimalFormat.format test in ar_EG");
684         testNumberFormatFormatting(nfEG, -123456789, "123,456,789-", "ar_EG");
685         testNumberFormatFormatting(nfEG, -456, "456-", "ar_EG");
686 
687         System.out.println("*** DecimalFormat.parse test in ar_EG");
688         testNumberFormatParsing(nfEG, "123-", -123L, "ar_EG");
689         testNumberFormatParsing(nfEG, "123--",-123L, "ar_EG");
690         testNumberFormatParsingCheckException(nfEG, "-123", 0, "ar_EG");
691 
692         System.out.println("*** DecimalFormat.format test in en_US");
693         testNumberFormatFormatting(nfUS, -123456789, "-123,456,789", "en_US");
694         testNumberFormatFormatting(nfUS, -456, "-456", "en_US");
695 
696         System.out.println("*** DecimalFormat.parse test in en_US");
697         testNumberFormatParsing(nfUS, "123-", 123L, "en_US");
698         testNumberFormatParsing(nfUS, "-123",-123L, "en_US");
699         testNumberFormatParsingCheckException(nfUS, "--123", 0, "en_US");
700     }
701 
702     private static void testNumberFormatFormatting(NumberFormat nf,
703                                                    int given,
704                                                    String expected,
705                                                    String locale) {
706         String str = nf.format(given);
707         if (expected.equals(str)) {
708             if (verbose) {
709                 System.out.print("  Passed: NumberFormat(" + locale);
710                 System.out.println(").format(" + given + ")");
711 
712                 System.out.println("      ---> \"" + str + "\"");
713             }
714         } else {
715             err = true;
716             System.err.print("  Failed: Unexpected NumberFormat(" + locale);
717             System.err.println(").format(" + given + ") result.");
718 
719             System.err.println("      Expected: \"" + expected + "\"");
720 
721             System.err.println("      Got:      \"" + str + "\"");
722         }
723     }
724 
725     private static void testNumberFormatParsing(NumberFormat nf,
726                                                 String given,
727                                                 Number expected,
728                                                 String locale) {
729         try {
730             Number n = nf.parse(given);
731             if (n.equals(expected)) {
732                 if (verbose) {
733                     System.out.print("  Passed: NumberFormat(" + locale);
734                     System.out.println(").parse(\"" + given + "\")");
735 
736                     System.out.println("      ---> " + n);
737                 }
738             } else {
739                 err = true;
740                 System.err.print("  Failed: Unexpected NumberFormat(" + locale);
741                 System.err.println(").parse(\"" + given + "\") result.");
742 
743                 System.err.println("      Expected: " + expected);
744 
745                 System.err.println("      Got:      " + n);
746             }
747         }
748         catch (ParseException pe) {
749             err = true;
750             System.err.print("  Failed: Unexpected exception with NumberFormat(");
751             System.err.println(locale + ").parse(\"" + given + "\") :");
752 
753             System.err.println("    " + pe);
754         }
755     }
756 
757     private static void testNumberFormatParsingCheckException(NumberFormat nf,
758                                                               String given,
759                                                               int expected,
760                                                               String locale) {
761         try {
762             Number n = nf.parse(given);
763             err = true;
764 
765             System.err.print("  Failed: NumberFormat(" + locale);
766             System.err.println(").parse(\"" + given + "\")");
767 
768             System.err.println("      should have thrown ParseException");
769         }
770         catch (ParseException pe) {
771             int errorOffset = pe.getErrorOffset();
772             if (errorOffset == expected) {
773                 if (verbose) {
774                     System.out.print("  Passed: NumberFormat(" + locale);
775                     System.out.println(").parse(\"" + given + "\")");
776 
777                     System.out.print("      threw ParseException as expected, and its errorOffset was correct: ");
778                     System.out.println(errorOffset);
779                 }
780             } else {
781                 err = true;
782                 System.err.print("  Failed: NumberFormat(" + locale);
783                 System.err.println(").parse(\"" + given + "\")");
784 
785                 System.err.print("      threw ParseException as expected, but its errorOffset was incorrect: ");
786                 System.err.println(errorOffset);
787             }
788         }
789     }
790 
791 }
792