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