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    *      https://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.apache.commons.lang3.LangAssertions.assertIllegalArgumentException;
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertNotEquals;
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   * Tests {@link org.apache.commons.lang3.time.FastDatePrinter}.
41   */
42  class FastDatePrinterTest extends AbstractLangTest {
43  
44      private enum Expected1806 {
45          India(INDIA, "+05", "+0530", "+05:30"), Greenwich(TimeZones.GMT, "Z", "Z", "Z"), NewYork(NEW_YORK, "-05", "-0500", "-05:00");
46  
47          final TimeZone zone;
48  
49          final String one;
50          final String two;
51          final String three;
52  
53          Expected1806(final TimeZone zone, final String one, final String two, final String three) {
54              this.zone = zone;
55              this.one = one;
56              this.two = two;
57              this.three = three;
58          }
59      }
60  
61      private static final String YYYY_MM_DD = "yyyy/MM/dd";
62      private static final TimeZone NEW_YORK = TimeZones.getTimeZone("America/New_York");
63      private static final TimeZone INDIA = TimeZones.getTimeZone("Asia/Calcutta");
64  
65      private static final Locale SWEDEN = new Locale("sv", "SE");
66  
67      private static Calendar initializeCalendar(final TimeZone tz) {
68          final Calendar cal = Calendar.getInstance(tz);
69          cal.set(Calendar.YEAR, 2001);
70          cal.set(Calendar.MONTH, 1); // not daylight savings
71          cal.set(Calendar.DAY_OF_MONTH, 4);
72          cal.set(Calendar.HOUR_OF_DAY, 12);
73          cal.set(Calendar.MINUTE, 8);
74          cal.set(Calendar.SECOND, 56);
75          cal.set(Calendar.MILLISECOND, 235);
76          return cal;
77      }
78  
79      private DatePrinter getDateInstance(final int dateStyle, final Locale locale) {
80          return getInstance(AbstractFormatCache.getPatternForStyle(Integer.valueOf(dateStyle), null, locale), TimeZone.getDefault(), Locale.getDefault());
81      }
82  
83      DatePrinter getInstance(final String format) {
84          return getInstance(format, TimeZone.getDefault(), Locale.getDefault());
85      }
86  
87      private DatePrinter getInstance(final String format, final Locale locale) {
88          return getInstance(format, TimeZone.getDefault(), locale);
89      }
90  
91      private DatePrinter getInstance(final String format, final TimeZone timeZone) {
92          return getInstance(format, timeZone, Locale.getDefault());
93      }
94  
95      /**
96       * Override this method in derived tests to change the construction of instances
97       *
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     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 
123     @Test
124     void test1806Argument() {
125         assertIllegalArgumentException(() -> getInstance("XXXX"));
126     }
127 
128     @Test
129     void testAppendableOptions() {
130         final DatePrinter format = getInstance("yyyy-MM-dd HH:mm:ss.SSS Z", TimeZones.GMT);
131         final Calendar calendar = Calendar.getInstance();
132         final StringBuilder sb = new StringBuilder();
133         final String expected = format.format(calendar, sb).toString();
134         sb.setLength(0);
135 
136         final Date date = calendar.getTime();
137         assertEquals(expected, format.format(date, sb).toString());
138         sb.setLength(0);
139 
140         final long epoch = date.getTime();
141         assertEquals(expected, format.format(epoch, sb).toString());
142     }
143 
144     @Test
145     void testDayNumberOfWeek() {
146         final DatePrinter printer = getInstance("u");
147         final Calendar calendar = Calendar.getInstance();
148 
149         calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
150         assertEquals("1", printer.format(calendar.getTime()));
151 
152         calendar.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
153         assertEquals("6", printer.format(calendar.getTime()));
154 
155         calendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
156         assertEquals("7", printer.format(calendar.getTime()));
157     }
158 
159     @Test
160     void testEquals() {
161         final DatePrinter printer1 = getInstance(YYYY_MM_DD);
162         final DatePrinter printer2 = getInstance(YYYY_MM_DD);
163 
164         assertEquals(printer1, printer2);
165         assertEquals(printer1.hashCode(), printer2.hashCode());
166 
167         assertNotEquals(printer1, new Object());
168     }
169 
170     @DefaultLocale(language = "en", country = "US")
171     @DefaultTimeZone("America/New_York")
172     @Test
173     void testFormat() {
174         final GregorianCalendar cal1 = new GregorianCalendar(2003, 0, 10, 15, 33, 20);
175         final GregorianCalendar cal2 = new GregorianCalendar(2003, 6, 10, 9, 0, 0);
176         final Date date1 = cal1.getTime();
177         final Date date2 = cal2.getTime();
178         final long millis1 = date1.getTime();
179         final long millis2 = date2.getTime();
180 
181         DatePrinter fdf = getInstance("yyyy-MM-dd'T'HH:mm:ss");
182         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
183         assertEquals(sdf.format(date1), fdf.format(date1));
184         assertEquals("2003-01-10T15:33:20", fdf.format(date1));
185         assertEquals("2003-01-10T15:33:20", fdf.format(cal1));
186         assertEquals("2003-01-10T15:33:20", fdf.format(millis1));
187         assertEquals("2003-07-10T09:00:00", fdf.format(date2));
188         assertEquals("2003-07-10T09:00:00", fdf.format(cal2));
189         assertEquals("2003-07-10T09:00:00", fdf.format(millis2));
190 
191         fdf = getInstance("Z");
192         assertEquals("-0500", fdf.format(date1));
193         assertEquals("-0500", fdf.format(cal1));
194         assertEquals("-0500", fdf.format(millis1));
195 
196         assertEquals("-0400", fdf.format(date2));
197         assertEquals("-0400", fdf.format(cal2));
198         assertEquals("-0400", fdf.format(millis2));
199 
200         fdf = getInstance("ZZ");
201         assertEquals("-05:00", fdf.format(date1));
202         assertEquals("-05:00", fdf.format(cal1));
203         assertEquals("-05:00", fdf.format(millis1));
204 
205         assertEquals("-04:00", fdf.format(date2));
206         assertEquals("-04:00", fdf.format(cal2));
207         assertEquals("-04:00", fdf.format(millis2));
208 
209         final String pattern = "GGGG GGG GG G yyyy yyy yy y MMMM MMM MM M LLLL LLL LL L"
210                 + " dddd ddd dd d DDDD DDD DD D EEEE EEE EE E aaaa aaa aa a zzzz zzz zz z";
211         fdf = getInstance(pattern);
212         sdf = new SimpleDateFormat(pattern);
213         // SDF bug fix starting with Java 7
214         assertEquals(sdf.format(date1).replace("2003 03 03 03", "2003 2003 03 2003"), fdf.format(date1));
215         assertEquals(sdf.format(date2).replace("2003 03 03 03", "2003 2003 03 2003"), fdf.format(date2));
216     }
217 
218     @Test
219     void testHourFormats() {
220         final Calendar calendar = Calendar.getInstance();
221         calendar.clear();
222         final DatePrinter printer = getInstance("K k H h");
223 
224         calendar.set(Calendar.HOUR_OF_DAY, 0);
225         assertEquals("0 24 0 12", printer.format(calendar));
226 
227         calendar.set(Calendar.HOUR_OF_DAY, 12);
228         assertEquals("0 12 12 12", printer.format(calendar));
229 
230         calendar.set(Calendar.HOUR_OF_DAY, 23);
231         assertEquals("11 23 23 11", printer.format(calendar));
232     }
233 
234     @Test
235     void testLang1103() {
236         final Calendar cal = Calendar.getInstance(SWEDEN);
237         cal.set(Calendar.DAY_OF_MONTH, 2);
238 
239         assertEquals("2", getInstance("d", SWEDEN).format(cal));
240         assertEquals("02", getInstance("dd", SWEDEN).format(cal));
241         assertEquals("002", getInstance("ddd", SWEDEN).format(cal));
242         assertEquals("0002", getInstance("dddd", SWEDEN).format(cal));
243         assertEquals("00002", getInstance("ddddd", SWEDEN).format(cal));
244     }
245 
246     @Test
247     void testLang303() {
248         final Calendar cal = Calendar.getInstance();
249         cal.set(2004, Calendar.DECEMBER, 31);
250 
251         DatePrinter format = getInstance(YYYY_MM_DD);
252         final String output = format.format(cal);
253 
254         format = SerializationUtils.deserialize(SerializationUtils.serialize((Serializable) format));
255         assertEquals(output, format.format(cal));
256     }
257 
258     @Test
259     void testLang538() {
260         // more commonly constructed with: cal = new GregorianCalendar(2009, 9, 16, 8, 42, 16)
261         // for the unit test to work in any time zone, constructing with GMT-8 rather than default locale time zone
262         final GregorianCalendar cal = new GregorianCalendar(TimeZones.getTimeZone("GMT-8"));
263         cal.clear();
264         cal.set(2009, Calendar.OCTOBER, 16, 8, 42, 16);
265 
266         final DatePrinter format = getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", TimeZones.GMT);
267         assertEquals("2009-10-16T16:42:16.000Z", format.format(cal.getTime()), "dateTime");
268         assertEquals("2009-10-16T16:42:16.000Z", format.format(cal), "dateTime");
269     }
270 
271     @Test
272     void testLang645() {
273         final Locale locale = new Locale("sv", "SE");
274 
275         final Calendar cal = Calendar.getInstance();
276         cal.set(2010, Calendar.JANUARY, 1, 12, 0, 0);
277         final Date d = cal.getTime();
278 
279         final DatePrinter fdf = getInstance("EEEE', week 'ww", locale);
280 
281         assertEquals("fredag, week 53", fdf.format(d));
282     }
283 
284     /**
285      * According to LANG-916 (https://issues.apache.org/jira/browse/LANG-916), 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     void testLang916() {
291 
292         final Calendar cal = Calendar.getInstance(TimeZones.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", TimeZones.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", TimeZones.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", TimeZones.getTimeZone("Europe/London")).format(cal);
307             assertEquals("2009-10-16T07:42:16 +0100", value, "calendar");
308         }
309     }
310 
311     @Test
312     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     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     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     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     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 This test confirms it, getting 366 back as a date
372      */
373     @Test
374     void testSimpleDate() {
375         final Calendar cal = Calendar.getInstance();
376         final DatePrinter format = getInstance(YYYY_MM_DD);
377 
378         cal.set(2004, Calendar.DECEMBER, 31);
379         assertEquals("2004/12/31", format.format(cal));
380         cal.set(999, Calendar.DECEMBER, 31);
381         assertEquals("0999/12/31", format.format(cal));
382         cal.set(1, Calendar.MARCH, 2);
383         assertEquals("0001/03/02", format.format(cal));
384     }
385 
386     @SuppressWarnings("deprecation")
387     @Test
388     void testStringBufferOptions() {
389         final DatePrinter format = getInstance("yyyy-MM-dd HH:mm:ss.SSS Z", TimeZones.GMT);
390         final Calendar calendar = Calendar.getInstance();
391         final StringBuffer sb = new StringBuffer();
392         final String expected = format.format(calendar, sb, new FieldPosition(0)).toString();
393         sb.setLength(0);
394         assertEquals(expected, format.format(calendar, sb).toString());
395         sb.setLength(0);
396 
397         final Date date = calendar.getTime();
398         assertEquals(expected, format.format(date, sb, new FieldPosition(0)).toString());
399         sb.setLength(0);
400         assertEquals(expected, format.format(date, sb).toString());
401         sb.setLength(0);
402 
403         final long epoch = date.getTime();
404         assertEquals(expected, format.format(epoch, sb, new FieldPosition(0)).toString());
405         sb.setLength(0);
406         assertEquals(expected, format.format(epoch, sb).toString());
407     }
408 
409     @DefaultTimeZone("UTC")
410     @Test
411     void testTimeZoneAsZ() {
412         final Calendar c = Calendar.getInstance(FastTimeZone.getGmtTimeZone());
413         final FastDateFormat noColonFormat = FastDateFormat.getInstance("Z");
414         assertEquals("+0000", noColonFormat.format(c));
415 
416         final FastDateFormat isoFormat = FastDateFormat.getInstance("ZZ");
417         assertEquals("Z", isoFormat.format(c));
418 
419         final FastDateFormat colonFormat = FastDateFormat.getInstance("ZZZ");
420         assertEquals("+00:00", colonFormat.format(c));
421     }
422 
423     @Test
424     void testTimeZoneMatches() {
425         final DatePrinter printer = getInstance(YYYY_MM_DD, NEW_YORK);
426         assertEquals(NEW_YORK, printer.getTimeZone());
427     }
428 
429     @Test
430     void testToStringContainsName() {
431         final DatePrinter printer = getInstance(YYYY_MM_DD);
432         assertTrue(printer.toString().startsWith("FastDate"));
433     }
434 
435     @DefaultLocale(language = "en", country = "US")
436     @DefaultTimeZone("America/New_York")
437     @Test
438     void testWeekYear() {
439         final GregorianCalendar cal = new GregorianCalendar(2020, 12, 31, 0, 0, 0);
440         final DatePrinter printer4Digits = getInstance("YYYY");
441         final DatePrinter printer4DigitsFallback = getInstance("YYY");
442         final DatePrinter printer2Digits = getInstance("YY");
443         final DatePrinter printer4DigitAnotherFallback = getInstance("Y");
444         assertEquals("2021", printer4Digits.format(cal));
445         assertEquals("2021", printer4DigitsFallback.format(cal));
446         assertEquals("2021", printer4DigitAnotherFallback.format(cal));
447         assertEquals("21", printer2Digits.format(cal));
448     }
449 }