1 /*
   2  * Copyright (c) 1998, 2023, 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 import java.text.*;
  25 import java.util.*;
  26 import java.io.*;
  27 
  28 /**
  29  * @test
  30  * @bug 4029195 4052408 4056591 4059917 4060212 4061287 4065240 4071441 4073003
  31  * 4089106 4100302 4101483 4103340 4103341 4104136 4104522 4106807 4108407
  32  * 4134203 4138203 4148168 4151631 4151706 4153860 4162071 4182066 4209272 4210209
  33  * 4213086 4250359 4253490 4266432 4406615 4413980 8008577 8305853
  34  * @library /java/text/testlib
  35  * @run main/othervm -Djava.locale.providers=COMPAT,SPI DateFormatRegression
  36  */
  37 public class DateFormatRegression extends IntlTest {
  38 
  39     public static void main(String[] args) throws Exception {
  40         new DateFormatRegression().run(args);
  41     }
  42 
  43     public void Test4029195() {
  44         @SuppressWarnings("deprecation")
  45         Date today = new Date();
  46 
  47         logln("today: " + today);
  48 
  49         SimpleDateFormat sdf = (SimpleDateFormat)SimpleDateFormat.getDateInstance();
  50         logln("pattern: " + sdf.toPattern());
  51         logln("today: " + sdf.format(today));
  52 
  53         sdf.applyPattern("G yyyy DDD");
  54         String todayS = sdf.format(today);
  55         logln("today: " + todayS);
  56         try {
  57             today = sdf.parse(todayS);
  58             logln("today date: " + today);
  59         } catch(Exception e) {
  60             logln("Error reparsing date: " + e.getMessage());
  61         }
  62 
  63         try {
  64             String rt = sdf.format(sdf.parse(todayS));
  65             logln("round trip: " + rt);
  66             if (!rt.equals(todayS)) errln("Fail: Want " + todayS + " Got " + rt);
  67         }
  68         catch (ParseException e) {
  69             errln("Fail: " + e);
  70             e.printStackTrace();
  71         }
  72     }
  73 
  74     public void Test4052408() {
  75         DateFormat fmt = DateFormat.getDateTimeInstance(DateFormat.SHORT,
  76                                                         DateFormat.SHORT, Locale.US);
  77         @SuppressWarnings("deprecation")
  78         Date date = new Date(97, Calendar.MAY, 3, 8, 55);
  79         String str;
  80         logln(str = fmt.format(date));
  81 
  82         if (!str.equals("5/3/97 8:55 AM"))
  83             errln("Fail: Test broken; Want 5/3/97 8:55 AM Got " + str);
  84         Map<Integer,String> expected = new HashMap<>();
  85         expected.put(DateFormat.MONTH_FIELD, "5");
  86         expected.put(DateFormat.DATE_FIELD, "3");
  87         expected.put(DateFormat.YEAR_FIELD, "97");
  88         expected.put(DateFormat.HOUR1_FIELD, "8");
  89         expected.put(DateFormat.MINUTE_FIELD, "55");
  90         expected.put(DateFormat.AM_PM_FIELD, "AM");
  91 
  92         StringBuffer buf = new StringBuffer();
  93         String fieldNames[] = {
  94             "ERA_FIELD",
  95             "YEAR_FIELD",
  96             "MONTH_FIELD",
  97             "DATE_FIELD",
  98             "HOUR_OF_DAY1_FIELD",
  99             "HOUR_OF_DAY0_FIELD",
 100             "MINUTE_FIELD",
 101             "SECOND_FIELD",
 102             "MILLISECOND_FIELD",
 103             "DAY_OF_WEEK_FIELD",
 104             "DAY_OF_YEAR_FIELD",
 105             "DAY_OF_WEEK_IN_MONTH_FIELD",
 106             "WEEK_OF_YEAR_FIELD",
 107             "WEEK_OF_MONTH_FIELD",
 108             "AM_PM_FIELD",
 109             "HOUR1_FIELD",
 110             "HOUR0_FIELD",
 111             "TIMEZONE_FIELD",
 112         };
 113         boolean pass = true;
 114         for (int i=0; i<=17; ++i) {
 115             FieldPosition pos = new FieldPosition(i);
 116             fmt.format(date, buf, pos);
 117             char[] dst = new char[pos.getEndIndex() - pos.getBeginIndex()];
 118             buf.getChars(pos.getBeginIndex(), pos.getEndIndex(), dst, 0);
 119             str = new String(dst);
 120             log(i + ": " + fieldNames[i] +
 121                              ", \"" + str + "\", " +
 122                              pos.getBeginIndex() + ", " +
 123                              pos.getEndIndex());
 124             String exp = expected.get(i);
 125             if ((exp == null && str.length() == 0) ||
 126                 str.equals(exp))
 127                 logln(" ok");
 128             else {
 129                 logln(" expected " + exp);
 130                 pass = false;
 131             }
 132         }
 133         if (!pass) errln("Fail: FieldPosition not set right by DateFormat");
 134     }
 135 
 136     /**
 137      * Verify the function of the [s|g]et2DigitYearStart() API.
 138      */
 139     @SuppressWarnings("deprecation")
 140     public void Test4056591() {
 141         try {
 142             SimpleDateFormat fmt = new SimpleDateFormat("yyMMdd", Locale.US);
 143             Date start = new Date(1809-1900, Calendar.DECEMBER, 25);
 144             fmt.set2DigitYearStart(start);
 145             if (!fmt.get2DigitYearStart().equals(start))
 146                 errln("get2DigitYearStart broken");
 147             Object[] DATA = {
 148                 "091225", new Date(1809-1900, Calendar.DECEMBER, 25),
 149                 "091224", new Date(1909-1900, Calendar.DECEMBER, 24),
 150                 "091226", new Date(1809-1900, Calendar.DECEMBER, 26),
 151                 "611225", new Date(1861-1900, Calendar.DECEMBER, 25),
 152             };
 153             for (int i=0; i<DATA.length; i+=2) {
 154                 String s = (String) DATA[i];
 155                 Date exp = (Date) DATA[i+1];
 156                 Date got = fmt.parse(s);
 157                 logln(s + " -> " + got + "; exp " + exp);
 158                 if (!got.equals(exp)) errln("set2DigitYearStart broken");
 159             }
 160         }
 161         catch (ParseException e) {
 162             errln("Fail: " + e);
 163             e.printStackTrace();
 164         }
 165     }
 166 
 167     public void Test4059917() {
 168         Locale locale = Locale.getDefault();
 169         if (!TestUtils.usesAsciiDigits(locale)) {
 170             logln("Skipping this test because locale is " + locale);
 171             return;
 172         }
 173 
 174         SimpleDateFormat fmt;
 175         String myDate;
 176 
 177         fmt = new SimpleDateFormat( "yyyy/MM/dd" );
 178         myDate = "1997/01/01";
 179         aux917( fmt, myDate );
 180 
 181         fmt = new SimpleDateFormat( "yyyyMMdd" );
 182         myDate = "19970101";
 183         aux917( fmt, myDate );
 184     }
 185 
 186     void aux917( SimpleDateFormat fmt, String str ) {
 187         try {
 188             logln( "==================" );
 189             logln( "testIt: pattern=" + fmt.toPattern() +
 190                    " string=" + str );
 191 
 192             Object o;
 193             o = fmt.parseObject( str );
 194             logln( "Parsed object: " + o );
 195 
 196             String formatted = fmt.format( o );
 197             logln( "Formatted string: " + formatted );
 198             if (!formatted.equals(str)) errln("Fail: Want " + str + " Got " + formatted);
 199         }
 200         catch (ParseException e) {
 201             errln("Fail: " + e);
 202             e.printStackTrace();
 203         }
 204     }
 205 
 206     public void Test4060212() {
 207       Locale savedLocale = Locale.getDefault();
 208       Locale.setDefault(Locale.US);
 209       try {
 210         String dateString = "1995-040.05:01:29";
 211 
 212         logln( "dateString= " + dateString );
 213         logln("Using yyyy-DDD.hh:mm:ss");
 214         SimpleDateFormat formatter = new SimpleDateFormat("yyyy-DDD.hh:mm:ss");
 215         ParsePosition pos = new ParsePosition(0);
 216         Date myDate = formatter.parse( dateString, pos );
 217         String myString = DateFormat.getDateTimeInstance( DateFormat.FULL,
 218                                                           DateFormat.LONG).format( myDate );
 219         logln( myString );
 220         Calendar cal = new GregorianCalendar();
 221         cal.setTime(myDate);
 222         if (cal.get(Calendar.DAY_OF_YEAR) != 40)
 223             errln("Fail: Got " + cal.get(Calendar.DAY_OF_YEAR) +
 224                                 " Want 40");
 225 
 226         logln("Using yyyy-ddd.hh:mm:ss");
 227         formatter = new SimpleDateFormat("yyyy-ddd.hh:mm:ss");
 228         pos = new ParsePosition(0);
 229         myDate = formatter.parse( dateString, pos );
 230         myString = DateFormat.getDateTimeInstance( DateFormat.FULL,
 231                                                    DateFormat.LONG).format( myDate );
 232         logln( myString );
 233         cal.setTime(myDate);
 234         if (cal.get(Calendar.DAY_OF_YEAR) != 40)
 235             errln("Fail: Got " + cal.get(Calendar.DAY_OF_YEAR) +
 236                                 " Want 40");
 237       }
 238       finally {
 239          Locale.setDefault(savedLocale);
 240       }
 241     }
 242 
 243     public void Test4061287() {
 244         SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
 245         try {
 246             logln(df.parse("35/01/1971").toString());
 247         }
 248         catch (ParseException e) {
 249             errln("Fail: " + e);
 250             e.printStackTrace();
 251         }
 252         df.setLenient(false);
 253         boolean ok = false;
 254         try {
 255             logln(df.parse("35/01/1971").toString());
 256         } catch (ParseException e) {ok=true;}
 257         if (!ok) errln("Fail: Lenient not working");
 258     }
 259 
 260     @SuppressWarnings("deprecation")
 261     public void Test4065240() {
 262         Date curDate;
 263         DateFormat shortdate, fulldate;
 264         String strShortDate, strFullDate;
 265         Locale saveLocale = Locale.getDefault();
 266         TimeZone saveZone = TimeZone.getDefault();
 267         try {
 268             Locale curLocale = Locale.GERMANY;
 269             Locale.setDefault(curLocale);
 270             TimeZone.setDefault(TimeZone.getTimeZone("EST"));
 271             curDate = new Date(98, 0, 1);
 272             shortdate = DateFormat.getDateInstance(DateFormat.SHORT);
 273             fulldate = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG
 274                                                       );
 275             strShortDate = new String("The current date (short form) is " + shortdate.
 276                                       format(curDate));
 277             strFullDate = new String("The current date (long form) is " + fulldate.format(curDate));
 278 
 279             logln(strShortDate);
 280             logln(strFullDate);
 281 
 282             // UPDATE THIS AS ZONE NAME RESOURCE FOR <EST> in de_DE is updated
 283             if (!strFullDate.endsWith("EST")
 284                     && !strFullDate.endsWith("GMT-05:00")) {
 285                 errln("Fail: Want GMT-05:00");
 286             }
 287         }
 288         finally {
 289             Locale.setDefault(saveLocale);
 290             TimeZone.setDefault(saveZone);
 291         }
 292     }
 293 
 294     /*
 295       DateFormat.equals is too narrowly defined.  As a result, MessageFormat
 296       does not work correctly.  DateFormat.equals needs to be written so
 297       that the Calendar sub-object is not compared using Calendar.equals,
 298       but rather compared for equivalency.  This may necessitate adding a
 299       (package private) method to Calendar to test for equivalency.
 300 
 301       Currently this bug breaks MessageFormat.toPattern
 302       */
 303     @SuppressWarnings("deprecation")
 304     public void Test4071441() {
 305         DateFormat fmtA = DateFormat.getInstance();
 306         DateFormat fmtB = DateFormat.getInstance();
 307         Calendar calA = fmtA.getCalendar();
 308         Calendar calB = fmtB.getCalendar();
 309         Date epoch = new Date(0);
 310         Date xmas = new Date(61, Calendar.DECEMBER, 25);
 311         calA.setTime(epoch);
 312         calB.setTime(epoch);
 313         if (!calA.equals(calB))
 314             errln("Fail: Can't complete test; Calendar instances unequal");
 315         if (!fmtA.equals(fmtB))
 316             errln("Fail: DateFormat unequal when Calendars equal");
 317         calB.setTime(xmas);
 318         if (calA.equals(calB))
 319             errln("Fail: Can't complete test; Calendar instances equal");
 320         if (!fmtA.equals(fmtB))
 321             errln("Fail: DateFormat unequal when Calendars equivalent");
 322         logln("DateFormat.equals ok");
 323     }
 324 
 325     /* The java.text.DateFormat.parse(String) method expects for the
 326       US locale a string formatted according to mm/dd/yy and parses it
 327       correctly.
 328 
 329       When given a string mm/dd/yyyy it only parses up to the first
 330       two y's, typically resulting in a date in the year 1919.
 331 
 332       Please extend the parsing method(s) to handle strings with
 333       four-digit year values (probably also applicable to various
 334       other locales.  */
 335     public void Test4073003() {
 336         try {
 337             DateFormat fmt = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US);
 338             String[] tests = { "12/25/61", "12/25/1961", "4/3/2010", "4/3/10" };
 339             for (int i=0; i<tests.length; i+=2) {
 340                 Date d = fmt.parse(tests[i]);
 341                 Date dd = fmt.parse(tests[i+1]);
 342                 String s = fmt.format(d);
 343                 String ss = fmt.format(dd);
 344                 if (!d.equals(dd))
 345                     errln("Fail: " + d + " != " + dd);
 346                 if (!s.equals(ss))
 347                     errln("Fail: " + s + " != " + ss);
 348                 logln("Ok: " + s + " " + d);
 349             }
 350         }
 351         catch (ParseException e) {
 352             errln("Fail: " + e);
 353             e.printStackTrace();
 354         }
 355     }
 356 
 357     public void Test4089106() {
 358         TimeZone def = TimeZone.getDefault();
 359         try {
 360             TimeZone customTz = TimeZone.getTimeZone("GMT-08:15");
 361             TimeZone.setDefault(customTz);
 362             SimpleDateFormat f = new SimpleDateFormat();
 363             if (!f.getTimeZone().equals(customTz))
 364                 errln("Fail: SimpleDateFormat should use TimeZone.getDefault()");
 365         }
 366         finally {
 367             TimeZone.setDefault(def);
 368         }
 369     }
 370 
 371     public void Test4100302() {
 372         Locale[] locales = new Locale[] {
 373             Locale.CANADA,
 374             Locale.CANADA_FRENCH,
 375             Locale.CHINA,
 376             Locale.CHINESE,
 377             Locale.ENGLISH,
 378             Locale.FRANCE,
 379             Locale.FRENCH,
 380             Locale.GERMAN,
 381             Locale.GERMANY,
 382             Locale.ITALIAN,
 383             Locale.ITALY,
 384             Locale.JAPAN,
 385             Locale.JAPANESE,
 386             Locale.KOREA,
 387             Locale.KOREAN,
 388             Locale.PRC,
 389             Locale.SIMPLIFIED_CHINESE,
 390             Locale.TAIWAN,
 391             Locale.TRADITIONAL_CHINESE,
 392             Locale.UK,
 393             Locale.US
 394             };
 395         try {
 396             boolean pass = true;
 397             for(int i = 0; i < locales.length; i++) {
 398 
 399                 Format format = DateFormat.getDateTimeInstance(DateFormat.FULL,
 400                                                                DateFormat.FULL, locales[i]);
 401                 byte[] bytes;
 402 
 403                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 404                 ObjectOutputStream oos = new ObjectOutputStream(baos);
 405 
 406                 oos.writeObject(format);
 407                 oos.flush();
 408 
 409                 baos.close();
 410                 bytes = baos.toByteArray();
 411 
 412                 ObjectInputStream ois =
 413                     new ObjectInputStream(new ByteArrayInputStream(bytes));
 414 
 415                 if (!format.equals(ois.readObject())) {
 416                     pass = false;
 417                     logln("DateFormat instance for locale " +
 418                           locales[i] + " is incorrectly serialized/deserialized.");
 419                 } else {
 420                     logln("DateFormat instance for locale " +
 421                           locales[i] + " is OKAY.");
 422                 }
 423             }
 424             if (!pass) errln("Fail: DateFormat serialization/equality bug");
 425         }
 426         catch (IOException e) {
 427             errln("Fail: " + e);
 428             e.printStackTrace();
 429         }
 430         catch (ClassNotFoundException e) {
 431             errln("Fail: " + e);
 432             e.printStackTrace();
 433         }
 434     }
 435 
 436     /**
 437      * Test whether DataFormat can be serialized/deserialized correctly
 438      * even if invalid/customized TimeZone is used.
 439      */
 440     public void Test4413980() {
 441         TimeZone savedTimeZone = TimeZone.getDefault();
 442         try {
 443             boolean pass = true;
 444             String[] IDs = new String[] {"Undefined", "PST", "US/Pacific",
 445                                          "GMT+3:00", "GMT-01:30"};
 446             for (int i = 0; i < IDs.length; i++) {
 447                 TimeZone tz = TimeZone.getTimeZone(IDs[i]);
 448                 TimeZone.setDefault(tz);
 449 
 450                 Format format = DateFormat.getDateTimeInstance(DateFormat.FULL,
 451                                                                DateFormat.FULL);
 452                 byte[] bytes;
 453 
 454                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 455                 ObjectOutputStream oos = new ObjectOutputStream(baos);
 456 
 457                 oos.writeObject(format);
 458                 oos.flush();
 459 
 460                 baos.close();
 461                 bytes = baos.toByteArray();
 462 
 463                 ObjectInputStream ois =
 464                     new ObjectInputStream(new ByteArrayInputStream(bytes));
 465 
 466                 if (!format.equals(ois.readObject())) {
 467                     pass = false;
 468                     logln("DateFormat instance which uses TimeZone <" +
 469                           IDs[i] + "> is incorrectly serialized/deserialized.");
 470                 } else {
 471                     logln("DateFormat instance which uses TimeZone <" +
 472                           IDs[i] + "> is correctly serialized/deserialized.");
 473                 }
 474             }
 475             if (!pass) {
 476                 errln("Fail: DateFormat serialization/equality bug");
 477             }
 478         }
 479         catch (IOException e) {
 480             errln("Fail: " + e);
 481             e.printStackTrace();
 482         }
 483         catch (ClassNotFoundException e) {
 484             errln("Fail: " + e);
 485             e.printStackTrace();
 486         }
 487         finally {
 488             TimeZone.setDefault(savedTimeZone);
 489         }
 490     }
 491 
 492     public void Test4101483() {
 493         SimpleDateFormat sdf = new SimpleDateFormat("z", Locale.US);
 494         FieldPosition fp = new FieldPosition(DateFormat.TIMEZONE_FIELD);
 495         @SuppressWarnings("deprecation")
 496         Date d= new Date(9234567890L);
 497         StringBuffer buf = new StringBuffer("");
 498         logln(sdf.format(d, buf, fp).toString());
 499         logln(d + " => " + buf);
 500         logln("beginIndex = " + fp.getBeginIndex());
 501         logln("endIndex = " + fp.getEndIndex());
 502         if (fp.getBeginIndex() == fp.getEndIndex()) errln("Fail: Empty field");
 503     }
 504 
 505     /**
 506      * Bug 4103340
 507      * Bug 4138203
 508      * This bug really only works in Locale.US, since that's what the locale
 509      * used for Date.toString() is.  Bug 4138203 reports that it fails on Korean
 510      * NT; it would actually have failed on any non-US locale.  Now it should
 511      * work on all locales.
 512      */
 513     public void Test4103340() {
 514         // choose a date that is the FIRST of some month
 515         // and some arbitrary time
 516         @SuppressWarnings("deprecation")
 517         Date d=new Date(97, 3, 1, 1, 1, 1);
 518         SimpleDateFormat df=new SimpleDateFormat("MMMM", Locale.US);
 519 
 520         String s = d.toString();
 521         String s2 = df.format(d);
 522         logln("Date="+s);
 523         logln("DF="+s2);
 524         if (s.indexOf(s2.substring(0,2)) == -1)
 525             errln("Months should match");
 526     }
 527 
 528     public void Test4103341() {
 529         TimeZone saveZone  =TimeZone.getDefault();
 530         try {
 531             TimeZone.setDefault(TimeZone.getTimeZone("CST"));
 532             SimpleDateFormat simple = new SimpleDateFormat("MM/dd/yyyy HH:mm");
 533             if (!simple.getTimeZone().equals(TimeZone.getDefault()))
 534                 errln("Fail: SimpleDateFormat not using default zone");
 535         }
 536         finally {
 537             TimeZone.setDefault(saveZone);
 538         }
 539     }
 540 
 541     public void Test4104136() {
 542         SimpleDateFormat sdf = new SimpleDateFormat();
 543         String pattern = "'time' hh:mm";
 544         sdf.applyPattern(pattern);
 545         logln("pattern: \"" + pattern + "\"");
 546 
 547         @SuppressWarnings("deprecation")
 548         Object[] DATA = {
 549             "time 10:30", new ParsePosition(10), new Date(70, Calendar.JANUARY, 1, 10, 30),
 550             "time 10:x", new ParsePosition(0), null,
 551             "time 10x", new ParsePosition(0), null,
 552         };
 553         for (int i=0; i<DATA.length; i+=3) {
 554             String text = (String) DATA[i];
 555             ParsePosition finish = (ParsePosition) DATA[i+1];
 556             Date exp = (Date) DATA[i+2];
 557 
 558             ParsePosition pos = new ParsePosition(0);
 559             Date d = sdf.parse(text, pos);
 560             logln(" text: \"" + text + "\"");
 561             logln(" index: " + pos.getIndex());
 562             logln(" result: " + d);
 563             if (pos.getIndex() != finish.getIndex())
 564                 errln("Fail: Expected pos " + finish.getIndex());
 565             if (!((d == null && exp == null) ||
 566                   d.equals(exp)))
 567                 errln("Fail: Expected result " + exp);
 568         }
 569     }
 570 
 571     /**
 572      * CANNOT REPRODUCE
 573      * According to the bug report, this test should throw a
 574      * StringIndexOutOfBoundsException during the second parse.  However,
 575      * this is not seen.
 576      */
 577     public void Test4104522() {
 578         SimpleDateFormat sdf = new SimpleDateFormat();
 579         String pattern = "'time' hh:mm";
 580         sdf.applyPattern(pattern);
 581         logln("pattern: \"" + pattern + "\"");
 582 
 583         // works correctly
 584         ParsePosition pp = new ParsePosition(0);
 585         String text = "time ";
 586         Date date = sdf.parse(text, pp);
 587         logln(" text: \"" + text + "\"" +
 588               " date: " + date);
 589 
 590         // works wrong
 591         pp = new ParsePosition(0);
 592         text = "time";
 593         date = sdf.parse(text, pp);
 594         logln(" text: \"" + text + "\"" +
 595               " date: " + date);
 596     }
 597 
 598     public void Test4106807() {
 599         Date date;
 600         DateFormat df = DateFormat.getDateTimeInstance();
 601         Object[] data = {
 602             new SimpleDateFormat("yyyyMMddHHmmss"),       "19980211140000",
 603             new SimpleDateFormat("yyyyMMddHHmmss'Z'"),    "19980211140000",
 604             new SimpleDateFormat("yyyyMMddHHmmss''"),     "19980211140000",
 605             new SimpleDateFormat("yyyyMMddHHmmss'a''a'"), "19980211140000a",
 606             new SimpleDateFormat("yyyyMMddHHmmss %"),     "19980211140000 ",
 607         };
 608         GregorianCalendar gc = new GregorianCalendar();
 609         TimeZone timeZone = TimeZone.getDefault();
 610 
 611         TimeZone gmt = (TimeZone)timeZone.clone();
 612 
 613         gmt.setRawOffset(0);
 614 
 615         for (int i=0; i<data.length; i+=2) {
 616             SimpleDateFormat format = (SimpleDateFormat) data[i];
 617             String dateString = (String) data[i+1];
 618             try {
 619                 format.setTimeZone(gmt);
 620                 date = format.parse(dateString);
 621                 logln(df.format(date));
 622                 gc.setTime(date);
 623                 logln("" + gc.get(Calendar.ZONE_OFFSET));
 624                 logln(format.format(date));
 625             }
 626             catch (ParseException e) {
 627                 logln("No way Jose");
 628             }
 629         }
 630     }
 631 
 632     /**
 633      * SimpleDateFormat won't parse "GMT"
 634      */
 635     public void Test4134203() {
 636         String dateFormat = "MM/dd/yy HH:mm:ss zzz";
 637         SimpleDateFormat fmt = new SimpleDateFormat(dateFormat);
 638         ParsePosition p0 = new ParsePosition(0);
 639         Date d = fmt.parse("01/22/92 04:52:00 GMT", p0);
 640         logln(d.toString());
 641         // In the failure case an exception is thrown by parse();
 642         // if no exception is thrown, the test passes.
 643     }
 644 
 645     /**
 646      * Another format for GMT string parse
 647      */
 648     public void Test4266432() {
 649         String dateFormat = "MM/dd HH:mm:ss zzz yyyy";
 650         SimpleDateFormat fmt = new SimpleDateFormat(dateFormat);
 651         ParsePosition p0 = new ParsePosition(0);
 652         Date d = fmt.parse("01/22 04:52:00 GMT 1992", p0);
 653         logln(d.toString());
 654         // In the failure case an exception is thrown by parse();
 655         // if no exception is thrown, the test passes.
 656     }
 657 
 658     /**
 659      * Millisecond field is limited to 3 digits; also test general millisecond
 660      * handling.
 661      *
 662      * NOTE: Updated for fixed semantics as of Kestrel.  See
 663      * 4253490
 664      */
 665     public void Test4148168() throws ParseException {
 666         SimpleDateFormat fmt = new SimpleDateFormat("", Locale.US);
 667         int ms = 456;
 668         String[] PAT = { "S", "SS", "SSS", "SSSS", "SSSSS",
 669                          "SSSSSSSSSSSSSSSSSSSS" };
 670         String[] OUT = { "456", "456", "456", "0456", "00456",
 671                          "00000000000000000456" };
 672         Calendar cal = Calendar.getInstance();
 673         cal.clear();
 674         cal.set(Calendar.MILLISECOND, ms);
 675         Date d = cal.getTime();
 676         for (int i=0; i<OUT.length; ++i) {
 677             fmt.applyPattern(PAT[i]);
 678             String str = fmt.format(d);
 679             if (!str.equals(OUT[i])) {
 680                 errln("FAIL: " + ms + " ms x \"" + PAT[i] + "\" -> \"" +
 681                       str + "\", exp \"" + OUT[i] + '"');
 682             }
 683         }
 684 
 685         // Test parsing
 686         fmt.applyPattern("s.S");
 687         String[] IN = { "1.4", "1.04", "1.004", "1.45", "1.456",
 688                         "1.4567", "1.45678" };
 689         int[] MS = { 4, 4, 4, 45, 456, 567, 678 };
 690         for (int i=0; i<IN.length; ++i) {
 691             d = fmt.parse(IN[i]);
 692             cal.setTime(d);
 693             ms = cal.get(Calendar.MILLISECOND);
 694             if (ms != MS[i]) {
 695                 errln("FAIL: parse(\"" + IN[i] + "\" x \"s.S\") -> " +
 696                       ms + " ms, exp " + MS[i] + " ms");
 697             }
 698         }
 699     }
 700 
 701     /**
 702      * SimpleDateFormat incorrect handling of 2 single quotes in format()
 703      */
 704     public void Test4151631() {
 705         String pattern = "'TO_DATE('''dd'-'MM'-'yyyy HH:mm:ss''' , ''DD-MM-YYYY HH:MI:SS'')'";
 706         logln("pattern=" + pattern);
 707         SimpleDateFormat format = new SimpleDateFormat(pattern, Locale.US);
 708         @SuppressWarnings("deprecation")
 709         String result = format.format(new Date(1998-1900, Calendar.JUNE, 30, 13, 30, 0));
 710         if (!result.equals("TO_DATE('30-06-1998 13:30:00' , 'DD-MM-YYYY HH:MI:SS')")) {
 711             errln("Fail: result=" + result);
 712         }
 713         else {
 714             logln("Pass: result=" + result);
 715         }
 716     }
 717 
 718     /**
 719      * 'z' at end of date format throws index exception in SimpleDateFormat
 720      * CANNOT REPRODUCE THIS BUG ON 1.2FCS
 721      */
 722     @SuppressWarnings("deprecation")
 723     public void Test4151706() {
 724         SimpleDateFormat fmt =
 725             new SimpleDateFormat("EEEE, dd-MMM-yy HH:mm:ss z", Locale.US);
 726         try {
 727             Date d = fmt.parse("Thursday, 31-Dec-98 23:00:00 GMT");
 728             if (d.getTime() != Date.UTC(1998-1900, Calendar.DECEMBER, 31, 23, 0, 0))
 729                 errln("Incorrect value: " + d);
 730         } catch (Exception e) {
 731             errln("Fail: " + e);
 732         }
 733     }
 734 
 735     /**
 736      * SimpleDateFormat fails to parse redundant data.
 737      * This is actually a bug down in GregorianCalendar, but it was reported
 738      * as follows...
 739      */
 740     public void Test4153860() throws ParseException {
 741       Locale savedLocale = Locale.getDefault();
 742       Locale.setDefault(Locale.US);
 743       try {
 744         SimpleDateFormat sf = (SimpleDateFormat)DateFormat.getDateTimeInstance();
 745         // Set the pattern
 746         sf.applyPattern("yyyy.MM-dd");
 747         // Try to create a Date for February 4th
 748         Date d1 = sf.parse("1998.02-04");
 749         // Set the pattern, this time to use the W value
 750         sf.applyPattern("yyyy.MM-dd W");
 751         // Try to create a Date for February 4th
 752         Date d2 = sf.parse("1998.02-04 1");
 753         if (!d1.equals(d2)) {
 754             errln("Parse failed, got " + d2 +
 755                   ", expected " + d1);
 756         }
 757       }
 758       finally {
 759         Locale.setDefault(savedLocale);
 760       }
 761     }
 762 
 763     /**
 764      * Confirm that "EST"(GMT-5:00) and "CST"(GMT-6:00) are used in US
 765      * as "EST" or "CST", not Australian "EST" and "CST".
 766      */
 767     @SuppressWarnings("deprecation")
 768     public void Test4406615() {
 769       Locale savedLocale = Locale.getDefault();
 770       TimeZone savedTimeZone = TimeZone.getDefault();
 771       Locale.setDefault(Locale.US);
 772       TimeZone.setDefault(TimeZone.getTimeZone("PST"));
 773 
 774       Date d1, d2;
 775       String dt = "Mon, 1 Jan 2001 00:00:00";
 776       SimpleDateFormat sdf =
 777         new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");
 778 
 779       try {
 780         d1 = sdf.parse(dt+" EST");
 781         d2 = sdf.parse(dt+" CST");
 782 
 783         if (d1.getYear() != (2000-1900) || d1.getMonth() != 11 ||
 784             d1.getDate() != 31 || d1.getHours() != 21 || d1.getMinutes() != 0 ||
 785             d2.getYear() != (2000-1900) || d2.getMonth() != 11 ||
 786             d2.getDate() != 31 || d2.getHours() != 22 || d2.getMinutes() != 0) {
 787             errln("Parse failed, d1 = " + d1 + ", d2 = " + d2);
 788         } else {
 789             logln("Parse passed");
 790         }
 791       }
 792       catch (Exception e) {
 793             errln("Parse failed, got Exception " + e);
 794       }
 795       finally {
 796         Locale.setDefault(savedLocale);
 797         TimeZone.setDefault(savedTimeZone);
 798       }
 799     }
 800 
 801     /**
 802      * Cannot reproduce this bug under 1.2 FCS -- it may be a convoluted duplicate
 803      * of some other bug that has been fixed.
 804      */
 805     public void Test4162071() {
 806         String dateString = "Thu, 30-Jul-1999 11:51:14 GMT";
 807         String format = "EEE', 'dd-MMM-yyyy HH:mm:ss z"; // RFC 822/1123
 808         SimpleDateFormat df = new
 809             SimpleDateFormat(format, Locale.US);
 810 
 811         try {
 812             Date x = df.parse(dateString);
 813             logln("Parse format \"" + format + "\" ok");
 814             logln(dateString + " -> " + df.format(x));
 815         } catch (Exception e) {
 816             errln("Parse format \"" + format + "\" failed.");
 817         }
 818     }
 819 
 820     /**
 821      * DateFormat shouldn't parse year "-1" as a two-digit year (e.g., "-1" -> 1999).
 822      */
 823     public void Test4182066() {
 824       Locale savedLocale = Locale.getDefault();
 825       Locale.setDefault(Locale.US);
 826       try {
 827         SimpleDateFormat fmt = new SimpleDateFormat("MM/dd/yy",
 828                                                     DateFormatSymbols.getInstance(Locale.US));
 829         SimpleDateFormat dispFmt = new SimpleDateFormat("MMM dd yyyy GG",
 830                                                         DateFormatSymbols.getInstance(Locale.US));
 831         /* We expect 2-digit year formats to put 2-digit years in the right
 832          * window.  Out of range years, that is, anything less than "00" or
 833          * greater than "99", are treated as literal years.  So "1/2/3456"
 834          * becomes 3456 AD.  Likewise, "1/2/-3" becomes -3 AD == 2 BC.
 835          */
 836         @SuppressWarnings("deprecation")
 837         Object[] DATA = {
 838             "02/29/00",   new Date(2000-1900, Calendar.FEBRUARY, 29),
 839             "01/23/01",   new Date(2001-1900, Calendar.JANUARY,  23),
 840             "04/05/-1",   new Date(  -1-1900, Calendar.APRIL,     5),
 841             "01/23/-9",   new Date(  -9-1900, Calendar.JANUARY,  23),
 842             "11/12/1314", new Date(1314-1900, Calendar.NOVEMBER, 12),
 843             "10/31/1",    new Date(   1-1900, Calendar.OCTOBER,  31),
 844             "09/12/+1",   null, // "+1" isn't recognized by US NumberFormat
 845             "09/12/001",  new Date(   1-1900, Calendar.SEPTEMBER,12),
 846         };
 847         StringBuffer out = new StringBuffer();
 848         boolean pass = true;
 849         for (int i=0; i<DATA.length; i+=2) {
 850             String str = (String) DATA[i];
 851             Date expected = (Date) DATA[i+1];
 852             Date actual;
 853             try {
 854                 actual = fmt.parse(str);
 855             } catch (ParseException e) {
 856                 actual = null;
 857             }
 858             String actStr = actual != null
 859                 ? dispFmt.format(actual) : String.valueOf(actual);
 860             if (expected == actual
 861                 || (expected != null && expected.equals(actual))) {
 862                 out.append(str + " => " + actStr + "\n");
 863             } else {
 864                 String expStr = expected != null
 865                     ? dispFmt.format(expected) : String.valueOf(expected);
 866                 out.append("FAIL: " + str + " => " + actStr
 867                            + ", expected " + expStr + "\n");
 868                 pass = false;
 869             }
 870         }
 871         if (pass) {
 872             log(out.toString());
 873         } else {
 874             err(out.toString());
 875         }
 876       }
 877       finally {
 878         Locale.setDefault(savedLocale);
 879       }
 880     }
 881 
 882     /**
 883      * Bug 4210209
 884      * Bug 4209272
 885      * DateFormat cannot parse Feb 29 2000 when setLenient(false)
 886      */
 887     public void Test4210209() {
 888         String pattern = "MMM d, yyyy";
 889         DateFormat fmt = new SimpleDateFormat(pattern,
 890                                               DateFormatSymbols.getInstance(Locale.US));
 891         fmt.getCalendar().setLenient(false);
 892         @SuppressWarnings("deprecation")
 893         Date d = new Date(2000-1900, Calendar.FEBRUARY, 29);
 894         String s = fmt.format(d);
 895         logln(d + " x " + pattern + " => " + s);
 896         ParsePosition pos = new ParsePosition(0);
 897         d = fmt.parse(s, pos);
 898         logln(d + " <= " + pattern + " x " + s);
 899         logln("Parse pos = " + pos);
 900         if (pos.getErrorIndex() != -1) {
 901             errln("FAIL");
 902         }
 903 
 904         // The underlying bug is in GregorianCalendar.  If the following lines
 905         // succeed, the bug is fixed.  If the bug isn't fixed, they will throw
 906         // an exception.
 907         GregorianCalendar cal = new GregorianCalendar();
 908         cal.clear();
 909         cal.setLenient(false);
 910         cal.set(2000, Calendar.FEBRUARY, 29); // This should work!
 911         logln(cal.getTime().toString());
 912     }
 913 
 914     /**
 915      * DateFormat.getDateTimeInstance() allows illegal parameters.
 916      */
 917     public void Test4213086() {
 918         int[] DATA = {
 919             // Style value, 0/1 for illegal/legal
 920             -99, 0,
 921              -1, 0,
 922               0, 1,
 923               1, 1,
 924               2, 1,
 925               3, 1,
 926               4, 0,
 927              99, 0,
 928         };
 929         String[] DESC = {
 930             "getDateTimeInstance(date)",
 931             "getDateTimeInstance(time)",
 932             "getDateInstance",
 933             "getTimeInstance",
 934         };
 935         String[] GOT = {
 936             "disallowed", "allowed", "<invalid>"
 937         };
 938         for (int i=0; i<DATA.length; i+=2) {
 939             int got = 2;
 940             for (int j=0; j<4; ++j) {
 941                 Exception e = null;
 942                 try {
 943                     DateFormat df;
 944                     switch (j) {
 945                     case 0:
 946                         df = DateFormat.getDateTimeInstance(DATA[i], 0);
 947                         break;
 948                     case 1:
 949                         df = DateFormat.getDateTimeInstance(0, DATA[i]);
 950                         break;
 951                     case 2:
 952                         df = DateFormat.getDateInstance(DATA[i]);
 953                         break;
 954                     case 3:
 955                         df = DateFormat.getTimeInstance(DATA[i]);
 956                         break;
 957                     }
 958                     got = 1;
 959                 } catch (IllegalArgumentException iae) {
 960                     got = 0;
 961                 } catch (Exception ex) {
 962                     e = ex;
 963                 }
 964                 if (got != DATA[i+1] || e != null) {
 965                     errln("FAIL: DateFormat." + DESC[j] + " style " + DATA[i] + " " +
 966                           (e != null ? e.toString() : GOT[got]));
 967                 }
 968             }
 969         }
 970     }
 971 
 972     @SuppressWarnings("deprecation")
 973     public void Test4253490() throws ParseException {
 974         SimpleDateFormat fmt = new SimpleDateFormat("S", Locale.US);
 975 
 976         GregorianCalendar cal = new GregorianCalendar();
 977 
 978         int      FORMAT_MS  = 12;
 979         String[] FORMAT_PAT = {  "S", "SS", "SSS", "SSSS" };
 980         String[] FORMAT_TO  = { "12", "12", "012", "0012" };
 981 
 982         String   PARSE_PAT  = "S";
 983         String[] PARSE_STR  = { "1", "12", "125", "1250" };
 984         int[]    PARSE_TO   = {  1,   12,   125,   250   };
 985         String   PARSE_LPAT  = "SSSSS";
 986 
 987         // Test formatting.  We want to make sure all digits are output
 988         // and that they are zero-padded on the left if necessary.
 989         cal.setTime(new Date(0L));
 990         cal.set(Calendar.MILLISECOND, FORMAT_MS);
 991         Date d = cal.getTime();
 992         for (int i=0; i<FORMAT_PAT.length; ++i) {
 993             fmt.applyPattern(FORMAT_PAT[i]);
 994             String s = fmt.format(d);
 995             if (s.equals(FORMAT_TO[i])) {
 996                 logln(String.valueOf(FORMAT_MS) + " ms f* \"" +
 997                       FORMAT_PAT[i] + "\" -> \"" + s + '"');
 998             } else {
 999                 errln("FAIL: " + FORMAT_MS + " ms f* \"" +
1000                       FORMAT_PAT[i] + "\" -> \"" + s + "\", expect \"" +
1001                       FORMAT_TO[i] + '"');
1002             }
1003         }
1004 
1005         // Test parsing.  We want to make sure all digits are read.
1006         fmt.applyPattern(PARSE_PAT);
1007         for (int i=0; i<PARSE_STR.length; ++i) {
1008             cal.setTime(fmt.parse(PARSE_STR[i]));
1009             int ms = cal.get(Calendar.MILLISECOND);
1010             if (ms == PARSE_TO[i]) {
1011                 logln("\"" + PARSE_STR[i] + "\" p* \"" +
1012                       PARSE_PAT + "\" -> " + ms + " ms");
1013             } else {
1014                 errln("FAIL: \"" + PARSE_STR[i] + "\" p* \"" +
1015                       PARSE_PAT + "\" -> " + ms + " ms, expect " +
1016                       PARSE_TO[i] + " ms");
1017             }
1018         }
1019 
1020         // Test LONG parsing.  We want to make sure all digits are read.
1021         fmt.applyPattern(PARSE_LPAT);
1022         for (int i=0; i<PARSE_STR.length; ++i) {
1023             cal.setTime(fmt.parse(PARSE_STR[i]));
1024             int ms = cal.get(Calendar.MILLISECOND);
1025             if (ms == PARSE_TO[i]) {
1026                 logln("\"" + PARSE_STR[i] + "\" p* \"" +
1027                       PARSE_LPAT + "\" -> " + ms + " ms");
1028             } else {
1029                 errln("FAIL: \"" + PARSE_STR[i] + "\" p* \"" +
1030                       PARSE_LPAT + "\" -> " + ms + " ms, expect " +
1031                       PARSE_TO[i] + " ms");
1032             }
1033         }
1034     }
1035 
1036     /**
1037      * Bug in handling of time instance; introduces in fix for 4213086.
1038      */
1039     public void Test4250359() {
1040         DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT,
1041                                                    Locale.US);
1042         @SuppressWarnings("deprecation")
1043         Date d = new Date(1999-1900, Calendar.DECEMBER, 25,
1044                           1, 2, 3);
1045         String s = df.format(d);
1046         // If the bug is present, we see "1:02 AM 1:02 AM".
1047         // Look for more than one instance of "AM".
1048         int i = s.indexOf("AM");
1049         int j = s.indexOf("AM", i+1);
1050         if (i < 0 || j >= 0) {
1051             errln("FAIL: getTimeInstance().format(d) => \"" +
1052                   s + "\"");
1053         }
1054     }
1055 
1056     /**
1057      * Test whether SimpleDataFormat (DateFormatSymbols) can format/parse
1058      * non-localized time zones.
1059      */
1060     public void Test4261506() {
1061         Locale savedLocale = Locale.getDefault();
1062         TimeZone savedTimeZone = TimeZone.getDefault();
1063         Locale.setDefault(Locale.JAPAN);
1064 
1065         // XXX: Test assumes "PST" is not TimeZoneNames_ja. Need to
1066         // pick up another time zone when L10N is done to that file.
1067         TimeZone.setDefault(TimeZone.getTimeZone("PST"));
1068         SimpleDateFormat fmt = new SimpleDateFormat("yy/MM/dd hh:ss zzz", Locale.JAPAN);
1069         @SuppressWarnings("deprecation")
1070         String result = fmt.format(new Date(1999 - 1900, 0, 1));
1071         logln("format()=>" + result);
1072         if (!result.endsWith("PST")) {
1073             errln("FAIL: SimpleDataFormat.format() did not retrun PST");
1074         }
1075 
1076         Date d = null;
1077         try {
1078             d = fmt.parse("99/1/1 10:10 PST");
1079         } catch (ParseException e) {
1080             errln("FAIL: SimpleDataFormat.parse() could not parse PST");
1081         }
1082 
1083         result = fmt.format(d);
1084         logln("roundtrip:" + result);
1085         if (!result.equals("99/01/01 10:10 PST")) {
1086             errln("FAIL: SimpleDataFomat timezone roundtrip failed");
1087         }
1088 
1089         Locale.setDefault(savedLocale);
1090         TimeZone.setDefault(savedTimeZone);
1091     }
1092 
1093 }
1094 
1095 //eof