1 /*
2  * Copyright (c) 2019, 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 8218948
27  * @summary TCK tests that check the time zone names between DFS.getZoneStrings()
28  *      and SDF.format("z*")
29  * @run main SDFTCKZoneNamesTest
30  */
31 import java.text.*;
32 import java.util.Calendar;
33 import java.util.Date;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.TimeZone;
37 
38 public class SDFTCKZoneNamesTest {
39 
myFormat(Date date, SimpleDateFormat sdf)40     StringBuffer myFormat(Date date, SimpleDateFormat sdf) {
41         String pattern = sdf.toPattern();
42         StringBuffer toAppendTo = new StringBuffer("");
43         boolean inQuote = false;
44         char prevCh = 0;
45         char ch;
46         int count = 0;
47         for (int i = 0; i < pattern.length(); i++) {
48             ch = pattern.charAt(i);
49             if (inQuote) {
50                 if (ch == '\'') {
51                     inQuote = false;
52                     if (count == 0) toAppendTo.append(ch);
53                     else count = 0;
54                 } else {
55                     toAppendTo.append(ch);
56                     count++;
57                 }
58             } else { // not inQuote
59                 if (ch == '\'') {
60                     inQuote = true;
61                     if (count > 0) {
62                         toAppendTo.append(subFormat(prevCh, count, date, sdf));
63                         count = 0;
64                         prevCh = 0;
65                     }
66                 } else if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z') {
67                     if (ch != prevCh && count > 0) {
68                         toAppendTo.append(subFormat(prevCh, count, date, sdf));
69                         prevCh = ch;
70                         count = 1;
71                     } else {
72                         if (ch != prevCh) prevCh = ch;
73                         count++;
74                     }
75                 } else if (count > 0) {
76                     toAppendTo.append(subFormat(prevCh, count, date, sdf));
77                     toAppendTo.append(ch);
78                     prevCh = 0;
79                     count = 0;
80                 } else toAppendTo.append(ch);
81             }
82         }
83         if (count > 0) {
84             toAppendTo.append(subFormat(prevCh, count, date, sdf));
85         }
86         return toAppendTo;
87     }
88 
subFormat(char ch, int count, Date date, SimpleDateFormat sdf)89     private String subFormat(char ch, int count, Date date, SimpleDateFormat sdf)
90             throws IllegalArgumentException {
91         int value = 0;
92         int patternCharIndex = -1;
93         int maxIntCount = 10;
94         String current = "";
95         DateFormatSymbols formatData = sdf.getDateFormatSymbols();
96         Calendar calendar = sdf.getCalendar();
97         calendar.setTime(date);
98         NumberFormat nf = sdf.getNumberFormat();
99         nf.setGroupingUsed(false);
100 
101         if ((patternCharIndex = "GyMdkHmsSEDFwWahKz".indexOf(ch)) == -1)
102             throw new IllegalArgumentException("Illegal pattern character " +
103                     "'" + ch + "'");
104         switch (patternCharIndex) {
105             case 0: // 'G' - ERA
106                 value = calendar.get(Calendar.ERA);
107                 current = formatData.getEras()[value];
108                 break;
109             case 1: // 'y' - YEAR
110                 value = calendar.get(Calendar.YEAR);
111 
112                 if (count == 2) {
113                     // For formatting, if the number of pattern letters is 2,
114                     // the year is truncated to 2 digits;
115                     current = zeroPaddingNumber(value, 2, 2, nf);
116                 } else {
117                     // otherwise it is interpreted as a number.
118                     current = zeroPaddingNumber(value, count, maxIntCount, nf);
119                 }
120 
121                 break;
122             case 2: // 'M' - MONTH
123                 value = calendar.get(Calendar.MONTH);
124                 if (count >= 4)
125                     // DateFormatSymbols::getMonths spec: "If the language requires different forms for formatting
126                     // and stand-alone usages, this method returns month names in the formatting form."
127                     // Because of that only formatting cases patterns may be tested. Like, "MMMM yyyy". Wrong
128                     // pattern: "MMMM".
129                     current = formatData.getMonths()[value];
130                 else if (count == 3)
131                     // DateFormatSymbols::getShortMonths spec: "If the language requires different forms for formatting
132                     // and stand-alone usages, This method returns short month names in the formatting form."
133                     // Because of that only formatting cases patterns may be tested. Like, "MMM yyyy". Wrong pattern:
134                     // "MMM".
135                     current = formatData.getShortMonths()[value];
136                 else
137                     current = zeroPaddingNumber(value + 1, count, maxIntCount, nf);
138                 break;
139             case 3: // 'd' - DATE
140                 value = calendar.get(Calendar.DATE);
141                 current = zeroPaddingNumber(value, count, maxIntCount, nf);
142                 break;
143             case 4: // 'k' - HOUR_OF_DAY: 1-based.  eg, 23:59 + 1 hour =>> 24:59
144                 if ((value = calendar.get(Calendar.HOUR_OF_DAY)) == 0)
145                     current = zeroPaddingNumber(
146                             calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1,
147                             count, maxIntCount, nf);
148                 else
149                     current = zeroPaddingNumber(value, count, maxIntCount, nf);
150                 break;
151             case 5: // 'H' - HOUR_OF_DAY:0-based.  eg, 23:59 + 1 hour =>> 00:59
152                 value = calendar.get(Calendar.HOUR_OF_DAY);
153                 current = zeroPaddingNumber(value, count, maxIntCount, nf);
154                 break;
155             case 6: // 'm' - MINUTE
156                 value = calendar.get(Calendar.MINUTE);
157                 current = zeroPaddingNumber(value, count, maxIntCount, nf);
158                 break;
159             case 7: // 's' - SECOND
160                 value = calendar.get(Calendar.SECOND);
161                 current = zeroPaddingNumber(value, count, maxIntCount, nf);
162                 break;
163             case 8: // 'S' - MILLISECOND
164                 value = calendar.get(Calendar.MILLISECOND);
165         /*
166         if (count > 3)
167             value = value * (int) Math.pow(10, count - 3);
168         else if (count == 2)
169             value = (value + 5) / 10;
170         else if (count == 1)
171             value = (value + 50) / 100;
172         */
173                 current = zeroPaddingNumber(value, count, maxIntCount, nf);
174                 break;
175             case 9: // 'E' - DAY_OF_WEEK
176                 value = calendar.get(Calendar.DAY_OF_WEEK);
177                 if (count >= 4)
178                     current = formatData.getWeekdays()[value];
179                 else // count < 4, use abbreviated form if exists
180                     current = formatData.getShortWeekdays()[value];
181                 break;
182             case 10:    // 'D' - DAY_OF_YEAR
183                 value = calendar.get(Calendar.DAY_OF_YEAR);
184                 current = zeroPaddingNumber(value, count, maxIntCount, nf);
185                 break;
186             case 11:   // 'F' - DAY_OF_WEEK_IN_MONTH
187                 value = calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH);
188                 current = zeroPaddingNumber(value, count, maxIntCount, nf);
189                 break;
190             case 12:    // 'w' - WEEK_OF_YEAR
191                 value = calendar.get(Calendar.WEEK_OF_YEAR);
192                 current = zeroPaddingNumber(value, count, maxIntCount, nf);
193                 break;
194             case 13:    // 'W' - WEEK_OF_MONTH
195                 value = calendar.get(Calendar.WEEK_OF_MONTH);
196                 current = zeroPaddingNumber(value, count, maxIntCount, nf);
197                 break;
198             case 14:    // 'a' - AM_PM
199                 value = calendar.get(Calendar.AM_PM);
200                 current = formatData.getAmPmStrings()[value];
201                 break;
202             case 15: // 'h' - HOUR:1-based.  eg, 11PM + 1 hour =>> 12 AM
203                 if ((value = calendar.get(Calendar.HOUR)) == 0)
204                     current = zeroPaddingNumber(
205                             calendar.getLeastMaximum(Calendar.HOUR) + 1,
206                             count, maxIntCount, nf);
207                 else
208                     current = zeroPaddingNumber(value, count, maxIntCount, nf);
209                 break;
210             case 16: // 'K' - HOUR: 0-based.  eg, 11PM + 1 hour =>> 0 AM
211                 value = calendar.get(Calendar.HOUR);
212                 current = zeroPaddingNumber(value, count, maxIntCount, nf);
213                 break;
214             case 17: // 'z' - ZONE_OFFSET
215                 int zoneIndex = getZoneIndex(calendar.getTimeZone().getID(), formatData);
216                 if (zoneIndex == -1) {
217                     StringBuffer zoneString = new StringBuffer();
218                     value = calendar.get(Calendar.ZONE_OFFSET)
219                             + calendar.get(Calendar.DST_OFFSET);
220                     if (value < 0) {
221                         zoneString.append("GMT-");
222                         value = -value; // suppress the '-' sign for text display.
223                     } else
224                         zoneString.append("GMT+");
225                     zoneString.append(
226                             zeroPaddingNumber((int) (value / (60 * 60 * 1000)), 2, 2, nf));
227                     zoneString.append(':');
228                     zoneString.append(
229                             zeroPaddingNumber(
230                                     (int) ((value % (60 * 60 * 1000)) / (60 * 1000)), 2, 2, nf));
231                     current = zoneString.toString();
232                 } else if (calendar.get(Calendar.DST_OFFSET) != 0) {
233                     if (count >= 4)
234                         current = formatData.getZoneStrings()[zoneIndex][3];
235                     else
236                         // count < 4, use abbreviated form if exists
237                         current = formatData.getZoneStrings()[zoneIndex][4];
238                 } else {
239                     if (count >= 4)
240                         current = formatData.getZoneStrings()[zoneIndex][1];
241                     else
242                         current = formatData.getZoneStrings()[zoneIndex][2];
243                 }
244                 break;
245         }
246 
247         return current;
248     }
249 
250 
zeroPaddingNumber(long value, int minDigits, int maxDigits, NumberFormat nf)251     String zeroPaddingNumber(long value, int minDigits, int maxDigits,
252                              NumberFormat nf) {
253         nf.setMinimumIntegerDigits(minDigits);
254         nf.setMaximumIntegerDigits(maxDigits);
255         return nf.format(value);
256     }
257 
258 
getZoneIndex(String ID, DateFormatSymbols dfs)259     int getZoneIndex(String ID, DateFormatSymbols dfs) {
260         String[][] zoneStrings = dfs.getZoneStrings();
261 
262         for (int index = 0; index < zoneStrings.length; index++) {
263             if (ID.equalsIgnoreCase(zoneStrings[index][0])) return index;
264         }
265         return -1;
266     }
267 
268 
269     final int second = 1000;
270     final int minute = 60 * second;
271     final int hour = 60 * minute;
272     final int day = 24 * hour;
273     final int month = 30 * day;
274     final int year = 365 * day;
275     final int someday = 30 * year + 3 * month + 19 * day + 5 * hour;
276 
277 
278     /* standalone interface */
main(String argv[])279     public static void main(String argv[]) {
280         Locale defaultLocale = Locale.getDefault();
281         SDFTCKZoneNamesTest test = new SDFTCKZoneNamesTest();
282 
283         try {
284             List.of(Locale.ROOT,
285                     Locale.CHINA,
286                     Locale.forLanguageTag("es-419"),
287                     Locale.GERMANY,
288                     Locale.forLanguageTag("hi-IN"),
289                     Locale.JAPAN,
290                     Locale.TAIWAN,
291                     Locale.UK,
292                     Locale.US,
293                     Locale.forLanguageTag("uz-Cyrl-UZ"),
294                     Locale.forLanguageTag("zh-SG"),
295                     Locale.forLanguageTag("zh-HK"),
296                     Locale.forLanguageTag("zh-MO")).stream()
297                 .forEach(l -> {
298                     System.out.printf("Testing locale: %s%n", l);
299                     Locale.setDefault(l);
300                     test.SimpleDateFormat0062();
301                 });
302         } finally {
303             Locale.setDefault(defaultLocale);
304         }
305     }
306 
307 
308     /**
309      * Equivalence class partitioning
310      * with state, input and output values orientation
311      * for public StringBuffer format(Date date, StringBuffer result, FieldPosition fp),
312      * <br><b>pre-conditions</b>: patterns: { "'s0mething'z mm::hh,yyyy zz",
313      * "zzzz",
314      * "z"} (each pattern contains letter for TIMEZONE_FIELD),
315      * <br><b>date</b>: a Date object
316      * <br><b>result</b>: a string
317      * <br><b>fp</b>: a FieldPosition object with TIMEZONE_FIELD field
318      * <br><b>output</b>: formatted date as expected.
319      */
SimpleDateFormat0062()320     public void SimpleDateFormat0062() {
321         boolean passed = true;
322         String patterns[] = {"'s0mething'z mm::hh,yyyy zz",
323                 "zzzz",
324                 "z"};
325         SimpleDateFormat sdf = new SimpleDateFormat();
326         Date date = new Date(1234567890);
327         for (String[] tz : sdf.getDateFormatSymbols().getZoneStrings()) {
328             sdf.setTimeZone(TimeZone.getTimeZone(tz[0]));
329             for (int i = 0; i < patterns.length && passed; i++) {
330                 StringBuffer result = new StringBuffer("qwerty");
331                 FieldPosition fp = new FieldPosition(DateFormat.TIMEZONE_FIELD);
332                 sdf.applyPattern(patterns[i]);
333                 String expected = new
334                         StringBuffer("qwerty").append(myFormat(date,
335                         sdf)).toString();
336                 String formatted = sdf.format(date, result, fp).toString();
337 
338                 if (!expected.equals(formatted)) {
339                     System.out.println(
340                             "method format(date, StringBuffer, FieldPosition) formats wrong");
341                     System.out.println("  pattern: " + patterns[i]);
342                     System.out.println("  time zone ID:   " + tz[0]);
343                     System.out.println("  expected result:  " + expected);
344                     System.out.println("  formatted result: " + formatted);
345                     passed = false;
346                 }
347 
348                 if (passed && !expected.equals(result.toString())) {
349                     System.out.println(
350                             "method format(Date date, StringBuffer toAppendTo, FieldPosition fp) toAppendTo is not " +
351                                     "equal to output");
352                     System.out.println("  pattern: " + patterns[i]);
353                     System.out.println("  time zone ID:   " + tz[0]);
354                     System.out.println("  toAppendTo   : " + result);
355                     System.out.println("  formatted date: " + formatted);
356                     passed = false;
357                 }
358             }
359         }
360         if(passed)
361         {
362             System.out.println("PASSED : OKAY");
363         }else
364         {
365             throw new RuntimeException("FAILED");
366         }
367     }
368 }
369