1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.lang3.time; 18 19 import static org.junit.jupiter.api.Assertions.assertEquals; 20 import static org.junit.jupiter.api.Assertions.assertNotEquals; 21 import static org.junit.jupiter.api.Assertions.assertNotSame; 22 import static org.junit.jupiter.api.Assertions.assertSame; 23 import static org.junit.jupiter.api.Assertions.fail; 24 25 import java.text.FieldPosition; 26 import java.text.Format; 27 import java.text.ParsePosition; 28 import java.text.SimpleDateFormat; 29 import java.time.Instant; 30 import java.time.LocalDate; 31 import java.time.ZoneId; 32 import java.util.Date; 33 import java.util.Locale; 34 import java.util.TimeZone; 35 import java.util.concurrent.ExecutorService; 36 import java.util.concurrent.Executors; 37 import java.util.concurrent.TimeUnit; 38 import java.util.concurrent.atomic.AtomicInteger; 39 import java.util.concurrent.atomic.AtomicLongArray; 40 41 import org.apache.commons.lang3.AbstractLangTest; 42 import org.junit.jupiter.api.Test; 43 import org.junitpioneer.jupiter.DefaultLocale; 44 import org.junitpioneer.jupiter.DefaultTimeZone; 45 46 /** 47 * Unit tests {@link org.apache.commons.lang3.time.FastDateFormat}. 48 * 49 * @since 2.0 50 */ 51 public class FastDateFormatTest extends AbstractLangTest { 52 private static final int NTHREADS = 10; 53 54 private static final int NROUNDS = 10000; 55 56 final Locale FINNISH = Locale.forLanguageTag("fi"); 57 final Locale HUNGARIAN = Locale.forLanguageTag("hu"); 58 measureTime(final Format printer, final Format parser)59 private AtomicLongArray measureTime(final Format printer, final Format parser) throws InterruptedException { 60 final ExecutorService pool = Executors.newFixedThreadPool(NTHREADS); 61 final AtomicInteger failures = new AtomicInteger(0); 62 final AtomicLongArray totalElapsed = new AtomicLongArray(2); 63 try { 64 for (int i = 0; i < NTHREADS; ++i) { 65 pool.submit(() -> { 66 for (int j = 0; j < NROUNDS; ++j) { 67 try { 68 final Date date = new Date(); 69 70 final long t0Millis = System.currentTimeMillis(); 71 final String formattedDate = printer.format(date); 72 totalElapsed.addAndGet(0, System.currentTimeMillis() - t0Millis); 73 74 final long t1Millis = System.currentTimeMillis(); 75 final Object pd = parser.parseObject(formattedDate); 76 totalElapsed.addAndGet(1, System.currentTimeMillis() - t1Millis); 77 78 if (!date.equals(pd)) { 79 failures.incrementAndGet(); 80 } 81 } catch (final Exception e) { 82 failures.incrementAndGet(); 83 e.printStackTrace(); 84 } 85 } 86 }); 87 } 88 } finally { 89 pool.shutdown(); 90 // depending on the performance of the machine used to run the parsing, 91 // the tests can run for a while. It should however complete within 92 // 30 seconds. Might need increase on very slow machines. 93 if (!pool.awaitTermination(30, TimeUnit.SECONDS)) { 94 pool.shutdownNow(); 95 fail("did not complete tasks"); 96 } 97 } 98 assertEquals(0, failures.get()); 99 return totalElapsed; 100 } 101 102 @DefaultLocale(language = "en", country = "US") 103 @Test test_changeDefault_Locale_DateInstance()104 public void test_changeDefault_Locale_DateInstance() { 105 final FastDateFormat format1 = FastDateFormat.getDateInstance(FastDateFormat.FULL, Locale.GERMANY); 106 final FastDateFormat format2 = FastDateFormat.getDateInstance(FastDateFormat.FULL); 107 Locale.setDefault(Locale.GERMANY); 108 final FastDateFormat format3 = FastDateFormat.getDateInstance(FastDateFormat.FULL); 109 110 assertSame(Locale.GERMANY, format1.getLocale()); 111 assertEquals(Locale.US, format2.getLocale()); 112 assertSame(Locale.GERMANY, format3.getLocale()); 113 assertNotSame(format1, format2); 114 assertNotSame(format2, format3); 115 } 116 117 @DefaultLocale(language = "en", country = "US") 118 @Test test_changeDefault_Locale_DateTimeInstance()119 public void test_changeDefault_Locale_DateTimeInstance() { 120 final FastDateFormat format1 = FastDateFormat.getDateTimeInstance(FastDateFormat.FULL, FastDateFormat.FULL, Locale.GERMANY); 121 final FastDateFormat format2 = FastDateFormat.getDateTimeInstance(FastDateFormat.FULL, FastDateFormat.FULL); 122 Locale.setDefault(Locale.GERMANY); 123 final FastDateFormat format3 = FastDateFormat.getDateTimeInstance(FastDateFormat.FULL, FastDateFormat.FULL); 124 125 assertSame(Locale.GERMANY, format1.getLocale()); 126 assertEquals(Locale.US, format2.getLocale()); 127 assertSame(Locale.GERMANY, format3.getLocale()); 128 assertNotSame(format1, format2); 129 assertNotSame(format2, format3); 130 } 131 132 /* 133 * Only the cache methods need to be tested here. 134 * The print methods are tested by {@link FastDateFormat_PrinterTest} 135 * and the parse methods are tested by {@link FastDateFormat_ParserTest} 136 */ 137 @Test test_getInstance()138 public void test_getInstance() { 139 final FastDateFormat format1 = FastDateFormat.getInstance(); 140 final FastDateFormat format2 = FastDateFormat.getInstance(); 141 assertSame(format1, format2); 142 } 143 144 @Test test_getInstance_String()145 public void test_getInstance_String() { 146 final FastDateFormat format1 = FastDateFormat.getInstance("MM/DD/yyyy"); 147 final FastDateFormat format2 = FastDateFormat.getInstance("MM-DD-yyyy"); 148 final FastDateFormat format3 = FastDateFormat.getInstance("MM-DD-yyyy"); 149 150 assertNotSame(format1, format2); 151 assertSame(format2, format3); 152 assertEquals("MM/DD/yyyy", format1.getPattern()); 153 assertEquals(TimeZone.getDefault(), format1.getTimeZone()); 154 assertEquals(TimeZone.getDefault(), format2.getTimeZone()); 155 } 156 157 @DefaultLocale(language = "en", country = "US") 158 @Test test_getInstance_String_Locale()159 public void test_getInstance_String_Locale() { 160 final FastDateFormat format1 = FastDateFormat.getInstance("MM/DD/yyyy", Locale.GERMANY); 161 final FastDateFormat format2 = FastDateFormat.getInstance("MM/DD/yyyy"); 162 final FastDateFormat format3 = FastDateFormat.getInstance("MM/DD/yyyy", Locale.GERMANY); 163 164 assertNotSame(format1, format2); 165 assertSame(format1, format3); 166 assertEquals(Locale.GERMANY, format1.getLocale()); 167 } 168 169 @DefaultLocale(language = "en", country = "US") 170 @DefaultTimeZone("America/New_York") 171 @Test test_getInstance_String_TimeZone()172 public void test_getInstance_String_TimeZone() { 173 174 final FastDateFormat format1 = FastDateFormat.getInstance("MM/DD/yyyy", 175 TimeZone.getTimeZone("Atlantic/Reykjavik")); 176 final FastDateFormat format2 = FastDateFormat.getInstance("MM/DD/yyyy"); 177 final FastDateFormat format3 = FastDateFormat.getInstance("MM/DD/yyyy", TimeZone.getDefault()); 178 final FastDateFormat format4 = FastDateFormat.getInstance("MM/DD/yyyy", TimeZone.getDefault()); 179 final FastDateFormat format5 = FastDateFormat.getInstance("MM-DD-yyyy", TimeZone.getDefault()); 180 final FastDateFormat format6 = FastDateFormat.getInstance("MM-DD-yyyy"); 181 182 assertNotSame(format1, format2); 183 assertEquals(TimeZone.getTimeZone("Atlantic/Reykjavik"), format1.getTimeZone()); 184 assertEquals(TimeZone.getDefault(), format2.getTimeZone()); 185 assertSame(format3, format4); 186 assertNotSame(format3, format5); 187 assertNotSame(format4, format6); 188 } 189 190 @DefaultLocale(language = "en", country = "US") 191 @DefaultTimeZone("America/New_York") 192 @Test test_getInstance_String_TimeZone_Locale()193 public void test_getInstance_String_TimeZone_Locale() { 194 final FastDateFormat format1 = FastDateFormat.getInstance("MM/DD/yyyy", 195 TimeZone.getTimeZone("Atlantic/Reykjavik"), Locale.GERMANY); 196 final FastDateFormat format2 = FastDateFormat.getInstance("MM/DD/yyyy", Locale.GERMANY); 197 final FastDateFormat format3 = FastDateFormat.getInstance("MM/DD/yyyy", 198 TimeZone.getDefault(), Locale.GERMANY); 199 200 assertNotSame(format1, format2); 201 assertEquals(TimeZone.getTimeZone("Atlantic/Reykjavik"), format1.getTimeZone()); 202 assertEquals(TimeZone.getDefault(), format2.getTimeZone()); 203 assertEquals(TimeZone.getDefault(), format3.getTimeZone()); 204 assertEquals(Locale.GERMANY, format1.getLocale()); 205 assertEquals(Locale.GERMANY, format2.getLocale()); 206 assertEquals(Locale.GERMANY, format3.getLocale()); 207 } 208 209 @Test testCheckDefaults()210 public void testCheckDefaults() { 211 final FastDateFormat format = FastDateFormat.getInstance(); 212 final FastDateFormat medium = FastDateFormat.getDateTimeInstance(FastDateFormat.SHORT, FastDateFormat.SHORT); 213 assertEquals(medium, format); 214 215 final SimpleDateFormat sdf = new SimpleDateFormat(); 216 assertEquals(sdf.toPattern(), format.getPattern()); 217 218 assertEquals(Locale.getDefault(), format.getLocale()); 219 assertEquals(TimeZone.getDefault(), format.getTimeZone()); 220 } 221 222 @Test testCheckDifferingStyles()223 public void testCheckDifferingStyles() { 224 final FastDateFormat shortShort = FastDateFormat.getDateTimeInstance(FastDateFormat.SHORT, FastDateFormat.SHORT, Locale.US); 225 final FastDateFormat shortLong = FastDateFormat.getDateTimeInstance(FastDateFormat.SHORT, FastDateFormat.LONG, Locale.US); 226 final FastDateFormat longShort = FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.SHORT, Locale.US); 227 final FastDateFormat longLong = FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.LONG, Locale.US); 228 229 assertNotEquals(shortShort, shortLong); 230 assertNotEquals(shortShort, longShort); 231 assertNotEquals(shortShort, longLong); 232 assertNotEquals(shortLong, longShort); 233 assertNotEquals(shortLong, longLong); 234 assertNotEquals(longShort, longLong); 235 } 236 237 @Test testDateDefaults()238 public void testDateDefaults() { 239 assertEquals(FastDateFormat.getDateInstance(FastDateFormat.LONG, Locale.CANADA), 240 FastDateFormat.getDateInstance(FastDateFormat.LONG, TimeZone.getDefault(), Locale.CANADA)); 241 242 assertEquals(FastDateFormat.getDateInstance(FastDateFormat.LONG, TimeZone.getTimeZone("America/New_York")), 243 FastDateFormat.getDateInstance(FastDateFormat.LONG, TimeZone.getTimeZone("America/New_York"), Locale.getDefault())); 244 245 assertEquals(FastDateFormat.getDateInstance(FastDateFormat.LONG), 246 FastDateFormat.getDateInstance(FastDateFormat.LONG, TimeZone.getDefault(), Locale.getDefault())); 247 } 248 249 @Test testLANG_1152()250 public void testLANG_1152() { 251 final TimeZone utc = FastTimeZone.getGmtTimeZone(); 252 final Date date = new Date(Long.MAX_VALUE); 253 254 String dateAsString = FastDateFormat.getInstance("yyyy-MM-dd", utc, Locale.US).format(date); 255 assertEquals("292278994-08-17", dateAsString); 256 257 dateAsString = FastDateFormat.getInstance("dd/MM/yyyy", utc, Locale.US).format(date); 258 assertEquals("17/08/292278994", dateAsString); 259 } 260 @Test testLANG_1267()261 public void testLANG_1267() { 262 FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); 263 } 264 265 /** 266 * According to LANG-954 (https://issues.apache.org/jira/browse/LANG-954) this is broken in Android 2.1. 267 */ 268 @Test testLANG_954()269 public void testLANG_954() { 270 final String pattern = "yyyy-MM-dd'T'"; 271 FastDateFormat.getInstance(pattern); 272 } 273 274 @Test testParseSync()275 public void testParseSync() throws InterruptedException { 276 final String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS"; 277 final SimpleDateFormat inner = new SimpleDateFormat(pattern); 278 final Format sdf= new Format() { 279 private static final long serialVersionUID = 1L; 280 281 @Override 282 public StringBuffer format(final Object obj, 283 final StringBuffer toAppendTo, 284 final FieldPosition fieldPosition) { 285 synchronized(this) { 286 return inner.format(obj, toAppendTo, fieldPosition); 287 } 288 } 289 290 @Override 291 public Object parseObject(final String source, final ParsePosition pos) { 292 synchronized(this) { 293 return inner.parseObject(source, pos); 294 } 295 } 296 }; 297 final AtomicLongArray sdfTime= measureTime(sdf, sdf); 298 299 final Format fdf = FastDateFormat.getInstance(pattern); 300 final AtomicLongArray fdfTime= measureTime(fdf, fdf); 301 302 //System.out.println(">>FastDateFormatTest: FastDatePrinter:"+fdfTime.get(0)+" SimpleDateFormat:"+sdfTime.get(0)); 303 //System.out.println(">>FastDateFormatTest: FastDateParser:"+fdfTime.get(1)+" SimpleDateFormat:"+sdfTime.get(1)); 304 } 305 306 @Test testTimeDateDefaults()307 public void testTimeDateDefaults() { 308 assertEquals(FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.MEDIUM, Locale.CANADA), 309 FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.MEDIUM, TimeZone.getDefault(), Locale.CANADA)); 310 311 assertEquals(FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.MEDIUM, TimeZone.getTimeZone("America/New_York")), 312 FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.MEDIUM, TimeZone.getTimeZone("America/New_York"), Locale.getDefault())); 313 314 assertEquals(FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.MEDIUM), 315 FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.MEDIUM, TimeZone.getDefault(), Locale.getDefault())); 316 } 317 318 @Test testTimeDefaults()319 public void testTimeDefaults() { 320 assertEquals(FastDateFormat.getTimeInstance(FastDateFormat.LONG, Locale.CANADA), 321 FastDateFormat.getTimeInstance(FastDateFormat.LONG, TimeZone.getDefault(), Locale.CANADA)); 322 323 assertEquals(FastDateFormat.getTimeInstance(FastDateFormat.LONG, TimeZone.getTimeZone("America/New_York")), 324 FastDateFormat.getTimeInstance(FastDateFormat.LONG, TimeZone.getTimeZone("America/New_York"), Locale.getDefault())); 325 326 assertEquals(FastDateFormat.getTimeInstance(FastDateFormat.LONG), 327 FastDateFormat.getTimeInstance(FastDateFormat.LONG, TimeZone.getDefault(), Locale.getDefault())); 328 } 329 330 @Test testStandaloneShortMonthForm()331 public void testStandaloneShortMonthForm() { 332 final TimeZone utc = FastTimeZone.getGmtTimeZone(); 333 final Instant testInstant = LocalDate.of(1970, 9, 15).atStartOfDay(ZoneId.of("UTC")).toInstant(); 334 final Date date = Date.from(testInstant); 335 336 String dateAsString = FastDateFormat.getInstance("yyyy-LLL-dd", utc, Locale.GERMAN).format(date); 337 assertEquals("1970-Sep-15", dateAsString); 338 339 dateAsString = FastDateFormat.getInstance("yyyy-LLL-dd", utc, FINNISH).format(date); 340 assertEquals("1970-syys-15", dateAsString); 341 342 dateAsString = FastDateFormat.getInstance("yyyy-LLL-dd", utc, HUNGARIAN).format(date); 343 assertEquals("1970-szept.-15", dateAsString); 344 } 345 346 @Test testStandaloneLongMonthForm()347 public void testStandaloneLongMonthForm() { 348 final TimeZone utc = FastTimeZone.getGmtTimeZone(); 349 final Instant testInstant = LocalDate.of(1970, 9, 15).atStartOfDay(ZoneId.of("UTC")).toInstant(); 350 final Date date = Date.from(testInstant); 351 352 String dateAsString = FastDateFormat.getInstance("yyyy-LLLL-dd", utc, Locale.GERMAN).format(date); 353 assertEquals("1970-September-15", dateAsString); 354 355 dateAsString = FastDateFormat.getInstance("yyyy-LLLL-dd", utc, FINNISH).format(date); 356 assertEquals("1970-syyskuu-15", dateAsString); 357 358 dateAsString = FastDateFormat.getInstance("yyyy-LLLL-dd", utc, HUNGARIAN).format(date); 359 assertEquals("1970-szeptember-15", dateAsString); 360 } 361 } 362