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.assertThrows;
22  import static org.junit.jupiter.api.Assertions.assertTrue;
23  
24  import java.io.Serializable;
25  import java.text.FieldPosition;
26  import java.text.SimpleDateFormat;
27  import java.util.Calendar;
28  import java.util.Date;
29  import java.util.GregorianCalendar;
30  import java.util.Locale;
31  import java.util.TimeZone;
32  
33  import org.apache.commons.lang3.AbstractLangTest;
34  import org.apache.commons.lang3.SerializationUtils;
35  import org.junit.jupiter.api.Test;
36  import org.junitpioneer.jupiter.DefaultLocale;
37  import org.junitpioneer.jupiter.DefaultTimeZone;
38  
39  /**
40   * Unit tests {@link org.apache.commons.lang3.time.FastDatePrinter}.
41   *
42   * @since 3.0
43   */
44  public class FastDatePrinterTest extends AbstractLangTest {
45  
46      private enum Expected1806 {
47          India(INDIA, "+05", "+0530", "+05:30"), Greenwich(TimeZones.GMT, "Z", "Z", "Z"), NewYork(
48                  NEW_YORK, "-05", "-0500", "-05:00");
49  
50          final TimeZone zone;
51  
52          final String one;
53          final String two;
54          final String three;
55          Expected1806(final TimeZone zone, final String one, final String two, final String three) {
56              this.zone = zone;
57              this.one = one;
58              this.two = two;
59              this.three = three;
60          }
61      }
62      private static final String YYYY_MM_DD = "yyyy/MM/dd";
63      private static final TimeZone NEW_YORK = TimeZone.getTimeZone("America/New_York");
64      private static final TimeZone INDIA = TimeZone.getTimeZone("Asia/Calcutta");
65  
66      private static final Locale SWEDEN = new Locale("sv", "SE");
67  
68      private static Calendar initializeCalendar(final TimeZone tz) {
69          final Calendar cal = Calendar.getInstance(tz);
70          cal.set(Calendar.YEAR, 2001);
71          cal.set(Calendar.MONTH, 1); // not daylight savings
72          cal.set(Calendar.DAY_OF_MONTH, 4);
73          cal.set(Calendar.HOUR_OF_DAY, 12);
74          cal.set(Calendar.MINUTE, 8);
75          cal.set(Calendar.SECOND, 56);
76          cal.set(Calendar.MILLISECOND, 235);
77          return cal;
78      }
79  
80      private DatePrinter getDateInstance(final int dateStyle, final Locale locale) {
81          return getInstance(AbstractFormatCache.getPatternForStyle(Integer.valueOf(dateStyle), null, locale), TimeZone.getDefault(), Locale.getDefault());
82      }
83  
84      DatePrinter getInstance(final String format) {
85          return getInstance(format, TimeZone.getDefault(), Locale.getDefault());
86      }
87  
88      private DatePrinter getInstance(final String format, final Locale locale) {
89          return getInstance(format, TimeZone.getDefault(), locale);
90      }
91  
92      private DatePrinter getInstance(final String format, final TimeZone timeZone) {
93          return getInstance(format, timeZone, Locale.getDefault());
94      }
95  
96      /**
97       * Override this method in derived tests to change the construction of instances
98       * @param format the format string to use
99       * @param timeZone the time zone to use
100      * @param locale the locale to use
101      * @return the DatePrinter to use for testing
102      */
103     protected DatePrinter getInstance(final String format, final TimeZone timeZone, final Locale locale) {
104         return new FastDatePrinter(format, timeZone, locale);
105     }
106 
107     @Test
108     public void test1806() {
109         for (final Expected1806 trial : Expected1806.values()) {
110             final Calendar cal = initializeCalendar(trial.zone);
111 
112             DatePrinter printer = getInstance("X", trial.zone);
113             assertEquals(trial.one, printer.format(cal));
114 
115             printer = getInstance("XX", trial.zone);
116             assertEquals(trial.two, printer.format(cal));
117 
118             printer = getInstance("XXX", trial.zone);
119             assertEquals(trial.three, printer.format(cal));
120         }
121     }
122     @Test
123     public void test1806Argument() {
124         assertThrows(IllegalArgumentException.class, () -> getInstance("XXXX"));
125     }
126 
127     @Test
128     public void testAppendableOptions() {
129         final DatePrinter format = getInstance("yyyy-MM-dd HH:mm:ss.SSS Z", TimeZones.GMT);
130         final Calendar calendar = Calendar.getInstance();
131         final StringBuilder sb = new StringBuilder();
132         final String expected = format.format(calendar, sb).toString();
133         sb.setLength(0);
134 
135         final Date date = calendar.getTime();
136         assertEquals(expected, format.format(date, sb).toString());
137         sb.setLength(0);
138 
139         final long epoch = date.getTime();
140         assertEquals(expected, format.format(epoch, sb).toString());
141     }
142 
143     @Test
144     public void testDayNumberOfWeek() {
145         final DatePrinter printer = getInstance("u");
146         final Calendar calendar = Calendar.getInstance();
147 
148         calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
149         assertEquals("1", printer.format(calendar.getTime()));
150 
151         calendar.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
152         assertEquals("6", printer.format(calendar.getTime()));
153 
154         calendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
155         assertEquals("7", printer.format(calendar.getTime()));
156     }
157 
158     @Test
159     public void testEquals() {
160         final DatePrinter printer1= getInstance(YYYY_MM_DD);
161         final DatePrinter printer2= getInstance(YYYY_MM_DD);
162 
163         assertEquals(printer1, printer2);
164         assertEquals(printer1.hashCode(), printer2.hashCode());
165 
166         assertNotEquals(printer1, new Object());
167     }
168 
169     @DefaultLocale(language = "en", country = "US")
170     @DefaultTimeZone("America/New_York")
171     @Test
172     public void testFormat() {
173         final GregorianCalendar cal1 = new GregorianCalendar(2003, 0, 10, 15, 33, 20);
174         final GregorianCalendar cal2 = new GregorianCalendar(2003, 6, 10, 9, 0, 0);
175         final Date date1 = cal1.getTime();
176         final Date date2 = cal2.getTime();
177         final long millis1 = date1.getTime();
178         final long millis2 = date2.getTime();
179 
180         DatePrinter fdf = getInstance("yyyy-MM-dd'T'HH:mm:ss");
181         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
182         assertEquals(sdf.format(date1), fdf.format(date1));
183         assertEquals("2003-01-10T15:33:20", fdf.format(date1));
184         assertEquals("2003-01-10T15:33:20", fdf.format(cal1));
185         assertEquals("2003-01-10T15:33:20", fdf.format(millis1));
186         assertEquals("2003-07-10T09:00:00", fdf.format(date2));
187         assertEquals("2003-07-10T09:00:00", fdf.format(cal2));
188         assertEquals("2003-07-10T09:00:00", fdf.format(millis2));
189 
190         fdf = getInstance("Z");
191         assertEquals("-0500", fdf.format(date1));
192         assertEquals("-0500", fdf.format(cal1));
193         assertEquals("-0500", fdf.format(millis1));
194 
195         assertEquals("-0400", fdf.format(date2));
196         assertEquals("-0400", fdf.format(cal2));
197         assertEquals("-0400", fdf.format(millis2));
198 
199         fdf = getInstance("ZZ");
200         assertEquals("-05:00", fdf.format(date1));
201         assertEquals("-05:00", fdf.format(cal1));
202         assertEquals("-05:00", fdf.format(millis1));
203 
204         assertEquals("-04:00", fdf.format(date2));
205         assertEquals("-04:00", fdf.format(cal2));
206         assertEquals("-04:00", fdf.format(millis2));
207 
208         final String pattern = "GGGG GGG GG G yyyy yyy yy y MMMM MMM MM M LLLL LLL LL L" +
209                 " dddd ddd dd d DDDD DDD DD D EEEE EEE EE E aaaa aaa aa a zzzz zzz zz z";
210         fdf = getInstance(pattern);
211         sdf = new SimpleDateFormat(pattern);
212         // SDF bug fix starting with Java 7
213         assertEquals(sdf.format(date1).replace("2003 03 03 03", "2003 2003 03 2003"), fdf.format(date1));
214         assertEquals(sdf.format(date2).replace("2003 03 03 03", "2003 2003 03 2003"), fdf.format(date2));
215     }
216 
217     @Test
218     public void testHourFormats() {
219         final Calendar calendar = Calendar.getInstance();
220         calendar.clear();
221         final DatePrinter printer = getInstance("K k H h");
222 
223         calendar.set(Calendar.HOUR_OF_DAY, 0);
224         assertEquals("0 24 0 12", printer.format(calendar));
225 
226         calendar.set(Calendar.HOUR_OF_DAY, 12);
227         assertEquals("0 12 12 12", printer.format(calendar));
228 
229         calendar.set(Calendar.HOUR_OF_DAY, 23);
230         assertEquals("11 23 23 11", printer.format(calendar));
231     }
232 
233     @Test
234     public void testLang1103() {
235         final Calendar cal = Calendar.getInstance(SWEDEN);
236         cal.set(Calendar.DAY_OF_MONTH, 2);
237 
238         assertEquals("2", getInstance("d", SWEDEN).format(cal));
239         assertEquals("02", getInstance("dd", SWEDEN).format(cal));
240         assertEquals("002", getInstance("ddd", SWEDEN).format(cal));
241         assertEquals("0002", getInstance("dddd", SWEDEN).format(cal));
242         assertEquals("00002", getInstance("ddddd", SWEDEN).format(cal));
243     }
244 
245     @Test
246     public void testLang303() {
247         final Calendar cal = Calendar.getInstance();
248         cal.set(2004, Calendar.DECEMBER, 31);
249 
250         DatePrinter format = getInstance(YYYY_MM_DD);
251         final String output = format.format(cal);
252 
253         format = SerializationUtils.deserialize(SerializationUtils.serialize((Serializable) format));
254         assertEquals(output, format.format(cal));
255     }
256 
257     @Test
258     public void testLang538() {
259         // more commonly constructed with: cal = new GregorianCalendar(2009, 9, 16, 8, 42, 16)
260         // for the unit test to work in any time zone, constructing with GMT-8 rather than default locale time zone
261         final GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT-8"));
262         cal.clear();
263         cal.set(2009, Calendar.OCTOBER, 16, 8, 42, 16);
264 
265         final DatePrinter format = getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", TimeZones.GMT);
266         assertEquals("2009-10-16T16:42:16.000Z", format.format(cal.getTime()), "dateTime");
267         assertEquals("2009-10-16T16:42:16.000Z", format.format(cal), "dateTime");
268     }
269 
270     @Test
271     public void testLang645() {
272         final Locale locale = new Locale("sv", "SE");
273 
274         final Calendar cal = Calendar.getInstance();
275         cal.set(2010, Calendar.JANUARY, 1, 12, 0, 0);
276         final Date d = cal.getTime();
277 
278         final DatePrinter fdf = getInstance("EEEE', week 'ww", locale);
279 
280         assertEquals("fredag, week 53", fdf.format(d));
281     }
282 
283     /**
284      * According to LANG-916 (https://issues.apache.org/jira/browse/LANG-916),
285      * the format method did contain a bug: it did not use the TimeZone data.
286      *
287      * This method test that the bug is fixed.
288      */
289     @Test
290     public void testLang916() {
291 
292         final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Europe/Paris"));
293         cal.clear();
294         cal.set(2009, 9, 16, 8, 42, 16);
295 
296         // calendar fast.
297         {
298             final String value = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss Z", TimeZone.getTimeZone("Europe/Paris")).format(cal);
299             assertEquals("2009-10-16T08:42:16 +0200", value, "calendar");
300         }
301         {
302             final String value = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss Z", TimeZone.getTimeZone("Asia/Kolkata")).format(cal);
303             assertEquals("2009-10-16T12:12:16 +0530", value, "calendar");
304         }
305         {
306             final String value = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss Z", TimeZone.getTimeZone("Europe/London")).format(cal);
307             assertEquals("2009-10-16T07:42:16 +0100", value, "calendar");
308         }
309     }
310 
311     @Test
312     public void testLocaleMatches() {
313         final DatePrinter printer= getInstance(YYYY_MM_DD, SWEDEN);
314         assertEquals(SWEDEN, printer.getLocale());
315     }
316 
317     /**
318      * Tests that pre-1000AD years get padded with yyyy
319      */
320     @Test
321     public void testLowYearPadding() {
322         final Calendar cal = Calendar.getInstance();
323         final DatePrinter format = getInstance(YYYY_MM_DD);
324 
325         cal.set(1, Calendar.JANUARY, 1);
326         assertEquals("0001/01/01", format.format(cal));
327         cal.set(10, Calendar.JANUARY, 1);
328         assertEquals("0010/01/01", format.format(cal));
329         cal.set(100, Calendar.JANUARY, 1);
330         assertEquals("0100/01/01", format.format(cal));
331         cal.set(999, Calendar.JANUARY, 1);
332         assertEquals("0999/01/01", format.format(cal));
333     }
334 
335     /**
336      * Show Bug #39410 is solved
337      */
338     @Test
339     public void testMilleniumBug() {
340         final Calendar cal = Calendar.getInstance();
341         final DatePrinter format = getInstance("dd.MM.yyyy");
342 
343         cal.set(1000, Calendar.JANUARY, 1);
344         assertEquals("01.01.1000", format.format(cal));
345     }
346 
347     @Test
348     public void testPatternMatches() {
349         final DatePrinter printer= getInstance(YYYY_MM_DD);
350         assertEquals(YYYY_MM_DD, printer.getPattern());
351     }
352 
353     /**
354      * Test case for {@link FastDateParser#FastDateParser(String, TimeZone, Locale)}.
355      */
356     @Test
357     public void testShortDateStyleWithLocales() {
358         final Locale usLocale = Locale.US;
359         final Locale swedishLocale = new Locale("sv", "SE");
360         final Calendar cal = Calendar.getInstance();
361         cal.set(2004, Calendar.FEBRUARY, 3);
362         DatePrinter fdf = getDateInstance(FastDateFormat.SHORT, usLocale);
363         assertEquals("2/3/04", fdf.format(cal));
364 
365         fdf = getDateInstance(FastDateFormat.SHORT, swedishLocale);
366         assertEquals("2004-02-03", fdf.format(cal));
367 
368     }
369 
370     /**
371      * testLowYearPadding showed that the date was buggy
372      * This test confirms it, getting 366 back as a date
373      */
374     @Test
375     public void testSimpleDate() {
376         final Calendar cal = Calendar.getInstance();
377         final DatePrinter format = getInstance(YYYY_MM_DD);
378 
379         cal.set(2004, Calendar.DECEMBER, 31);
380         assertEquals("2004/12/31", format.format(cal));
381         cal.set(999, Calendar.DECEMBER, 31);
382         assertEquals("0999/12/31", format.format(cal));
383         cal.set(1, Calendar.MARCH, 2);
384         assertEquals("0001/03/02", format.format(cal));
385     }
386 
387     @SuppressWarnings("deprecation")
388     @Test
389     public void testStringBufferOptions() {
390         final DatePrinter format = getInstance("yyyy-MM-dd HH:mm:ss.SSS Z", TimeZones.GMT);
391         final Calendar calendar = Calendar.getInstance();
392         final StringBuffer sb = new StringBuffer();
393         final String expected = format.format(calendar, sb, new FieldPosition(0)).toString();
394         sb.setLength(0);
395         assertEquals(expected, format.format(calendar, sb).toString());
396         sb.setLength(0);
397 
398         final Date date = calendar.getTime();
399         assertEquals(expected, format.format(date, sb, new FieldPosition(0)).toString());
400         sb.setLength(0);
401         assertEquals(expected, format.format(date, sb).toString());
402         sb.setLength(0);
403 
404         final long epoch = date.getTime();
405         assertEquals(expected, format.format(epoch, sb, new FieldPosition(0)).toString());
406         sb.setLength(0);
407         assertEquals(expected, format.format(epoch, sb).toString());
408     }
409 
410     @DefaultTimeZone("UTC")
411     @Test
412     public void testTimeZoneAsZ() {
413         final Calendar c = Calendar.getInstance(FastTimeZone.getGmtTimeZone());
414         final FastDateFormat noColonFormat = FastDateFormat.getInstance("Z");
415         assertEquals("+0000", noColonFormat.format(c));
416 
417         final FastDateFormat isoFormat = FastDateFormat.getInstance("ZZ");
418         assertEquals("Z", isoFormat.format(c));
419 
420         final FastDateFormat colonFormat = FastDateFormat.getInstance("ZZZ");
421         assertEquals("+00:00", colonFormat.format(c));
422     }
423 
424     @Test
425     public void testTimeZoneMatches() {
426         final DatePrinter printer= getInstance(YYYY_MM_DD, NEW_YORK);
427         assertEquals(NEW_YORK, printer.getTimeZone());
428     }
429 
430     @Test
431     public void testToStringContainsName() {
432         final DatePrinter printer= getInstance(YYYY_MM_DD);
433         assertTrue(printer.toString().startsWith("FastDate"));
434     }
435 
436     @DefaultLocale(language = "en", country = "US")
437     @DefaultTimeZone("America/New_York")
438     @Test
439     public void testWeekYear() {
440         final GregorianCalendar cal = new GregorianCalendar(2020, 12, 31, 0, 0, 0);
441         final DatePrinter printer4Digits = getInstance("YYYY");
442         final DatePrinter printer4DigitsFallback = getInstance("YYY");
443         final DatePrinter printer2Digits = getInstance("YY");
444         final DatePrinter printer4DigitAnotherFallback = getInstance("Y");
445         assertEquals("2021", printer4Digits.format(cal));
446         assertEquals("2021", printer4DigitsFallback.format(cal));
447         assertEquals("2021", printer4DigitAnotherFallback.format(cal));
448         assertEquals("21", printer2Digits.format(cal));
449     }
450 }