View Javadoc
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  
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
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
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
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
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
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
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
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
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
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
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
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
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
269     public void testLANG_954() {
270         final String pattern = "yyyy-MM-dd'T'";
271         FastDateFormat.getInstance(pattern);
272     }
273 
274     @Test
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
307     public void testStandaloneLongMonthForm() {
308         final TimeZone utc = FastTimeZone.getGmtTimeZone();
309         final Instant testInstant = LocalDate.of(1970, 9, 15).atStartOfDay(ZoneId.of("UTC")).toInstant();
310         final Date date = Date.from(testInstant);
311 
312         String dateAsString = FastDateFormat.getInstance("yyyy-LLLL-dd", utc, Locale.GERMAN).format(date);
313         assertEquals("1970-September-15", dateAsString);
314 
315         dateAsString = FastDateFormat.getInstance("yyyy-LLLL-dd", utc, FINNISH).format(date);
316         assertEquals("1970-syyskuu-15", dateAsString);
317 
318         dateAsString = FastDateFormat.getInstance("yyyy-LLLL-dd", utc, HUNGARIAN).format(date);
319         assertEquals("1970-szeptember-15", dateAsString);
320     }
321 
322     @Test
323     public void testStandaloneShortMonthForm() {
324         final TimeZone utc = FastTimeZone.getGmtTimeZone();
325         final Instant testInstant = LocalDate.of(1970, 9, 15).atStartOfDay(ZoneId.of("UTC")).toInstant();
326         final Date date = Date.from(testInstant);
327 
328         String dateAsString = FastDateFormat.getInstance("yyyy-LLL-dd", utc, Locale.GERMAN).format(date);
329         assertEquals("1970-Sep-15", dateAsString);
330 
331         dateAsString = FastDateFormat.getInstance("yyyy-LLL-dd", utc, FINNISH).format(date);
332         assertEquals("1970-syys-15", dateAsString);
333 
334         dateAsString = FastDateFormat.getInstance("yyyy-LLL-dd", utc, HUNGARIAN).format(date);
335         assertEquals("1970-szept.-15", dateAsString);
336     }
337 
338     @Test
339     public void testTimeDateDefaults() {
340         assertEquals(FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.MEDIUM, Locale.CANADA),
341                 FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.MEDIUM, TimeZone.getDefault(), Locale.CANADA));
342 
343         assertEquals(FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.MEDIUM, TimeZone.getTimeZone("America/New_York")),
344                 FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.MEDIUM, TimeZone.getTimeZone("America/New_York"), Locale.getDefault()));
345 
346         assertEquals(FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.MEDIUM),
347                 FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.MEDIUM, TimeZone.getDefault(), Locale.getDefault()));
348     }
349 
350     @Test
351     public void testTimeDefaults() {
352         assertEquals(FastDateFormat.getTimeInstance(FastDateFormat.LONG, Locale.CANADA),
353                 FastDateFormat.getTimeInstance(FastDateFormat.LONG, TimeZone.getDefault(), Locale.CANADA));
354 
355         assertEquals(FastDateFormat.getTimeInstance(FastDateFormat.LONG, TimeZone.getTimeZone("America/New_York")),
356                 FastDateFormat.getTimeInstance(FastDateFormat.LONG, TimeZone.getTimeZone("America/New_York"), Locale.getDefault()));
357 
358         assertEquals(FastDateFormat.getTimeInstance(FastDateFormat.LONG),
359                 FastDateFormat.getTimeInstance(FastDateFormat.LONG, TimeZone.getDefault(), Locale.getDefault()));
360     }
361 }