1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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.assertFalse;
22 import static org.junit.jupiter.api.Assertions.assertNotEquals;
23 import static org.junit.jupiter.api.Assertions.assertNotNull;
24 import static org.junit.jupiter.api.Assertions.assertThrows;
25 import static org.junit.jupiter.api.Assertions.assertTrue;
26 import static org.junit.jupiter.api.Assertions.fail;
27
28 import java.io.Serializable;
29 import java.text.ParseException;
30 import java.text.ParsePosition;
31 import java.text.SimpleDateFormat;
32 import java.util.Calendar;
33 import java.util.Date;
34 import java.util.GregorianCalendar;
35 import java.util.HashMap;
36 import java.util.Locale;
37 import java.util.Map;
38 import java.util.TimeZone;
39 import java.util.stream.Stream;
40
41 import org.apache.commons.lang3.AbstractLangTest;
42 import org.apache.commons.lang3.LocaleUtils;
43 import org.apache.commons.lang3.SerializationUtils;
44 import org.apache.commons.lang3.SystemUtils;
45 import org.apache.commons.lang3.function.TriFunction;
46 import org.junit.jupiter.api.AfterEach;
47 import org.junit.jupiter.api.BeforeEach;
48 import org.junit.jupiter.api.Test;
49 import org.junit.jupiter.params.ParameterizedTest;
50 import org.junit.jupiter.params.provider.Arguments;
51 import org.junit.jupiter.params.provider.MethodSource;
52 import org.junitpioneer.jupiter.DefaultLocale;
53 import org.junitpioneer.jupiter.DefaultTimeZone;
54 import org.junitpioneer.jupiter.ReadsDefaultLocale;
55 import org.junitpioneer.jupiter.ReadsDefaultTimeZone;
56 import org.junitpioneer.jupiter.cartesian.ArgumentSets;
57 import org.junitpioneer.jupiter.cartesian.CartesianTest;
58
59
60
61
62 @DefaultLocale(language = "en")
63 @DefaultTimeZone(TimeZones.GMT_ID)
64 @ReadsDefaultLocale
65 @ReadsDefaultTimeZone
66 class FastDateParserTest extends AbstractLangTest {
67
68 private enum Expected1806 {
69
70
71 India(INDIA, "+05", "+0530", "+05:30", true),
72 Greenwich(TimeZones.GMT, "Z", "Z", "Z", false),
73 NewYork(NEW_YORK, "-05", "-0500", "-05:00", false);
74
75
76 final TimeZone zone;
77 final String one;
78 final String two;
79 final String three;
80 final long offset;
81
82 Expected1806(final TimeZone zone, final String one, final String two, final String three,
83 final boolean hasHalfHourOffset) {
84 this.zone = zone;
85 this.one = one;
86 this.two = two;
87 this.three = three;
88 this.offset = hasHalfHourOffset ? 30 * 60 * 1000 : 0;
89 }
90 }
91
92 static final String DATE_PARSER_PARAMETERS = "dateParserParameters";
93
94 static final String SHORT_FORMAT_NOERA = "y/M/d/h/a/m/s/E";
95
96 static final String LONG_FORMAT_NOERA = "yyyy/MMMM/dddd/hhhh/mmmm/ss/aaaa/EEEE";
97 static final String SHORT_FORMAT = "G/" + SHORT_FORMAT_NOERA;
98 static final String LONG_FORMAT = "GGGG/" + LONG_FORMAT_NOERA;
99
100 private static final String yMdHmsSZ = "yyyy-MM-dd'T'HH:mm:ss.SSS Z";
101 private static final String DMY_DOT = "dd.MM.yyyy";
102 private static final String YMD_SLASH = "yyyy/MM/dd";
103 private static final String MDY_DASH = "MM-DD-yyyy";
104 private static final String MDY_SLASH = "MM/DD/yyyy";
105
106 private static final TimeZone REYKJAVIK = TimeZones.getTimeZone("Atlantic/Reykjavik");
107 private static final TimeZone NEW_YORK = TimeZones.getTimeZone("America/New_York");
108 private static final TimeZone INDIA = TimeZones.getTimeZone("Asia/Calcutta");
109
110 private static final Locale SWEDEN = new Locale("sv", "SE");
111
112 static void checkParse(final Locale locale, final Calendar cal, final SimpleDateFormat simpleDateFormat,
113 final DateParser dateParser) {
114 final String formattedDate = simpleDateFormat.format(cal.getTime());
115 checkParse(locale, simpleDateFormat, dateParser, formattedDate, formattedDate);
116 checkParse(locale, simpleDateFormat, dateParser, formattedDate.toLowerCase(locale), formattedDate);
117 checkParse(locale, simpleDateFormat, dateParser, formattedDate.toUpperCase(locale), formattedDate);
118 }
119
120 static void checkParse(final Locale locale, final SimpleDateFormat simpleDateFormat, final DateParser dateParser,
121 final String formattedDate, final String originalFormattedDate) {
122 try {
123 final Date expectedTime = simpleDateFormat.parse(formattedDate);
124 final Date actualTime = dateParser.parse(formattedDate);
125 assertEquals(expectedTime, actualTime,
126 "locale: " + locale + ", formattedDate: '" + formattedDate + "', originalFormattedDate: '"
127 + originalFormattedDate + ", simpleDateFormat.pattern: '" + simpleDateFormat + "', Java: "
128 + SystemUtils.JAVA_RUNTIME_VERSION + "\n");
129 } catch (final Exception e) {
130 fail("locale: " + locale + ", formattedDate: '" + formattedDate + "', error : " + e + "\n", e);
131 }
132 }
133
134 static Stream<Arguments> dateParserParameters() {
135 return Stream.of(
136
137 Arguments.of((TriFunction<String, TimeZone, Locale, DateParser>) (format, timeZone, locale)
138 -> new FastDateParser(format, timeZone, locale, null)),
139 Arguments.of((TriFunction<String, TimeZone, Locale, DateParser>) FastDateFormat::getInstance)
140
141 );
142 }
143
144 private static Calendar initializeCalendar(final TimeZone timeZone) {
145 final Calendar cal = Calendar.getInstance(timeZone);
146 cal.set(Calendar.YEAR, 2001);
147 cal.set(Calendar.MONTH, 1);
148 cal.set(Calendar.DAY_OF_MONTH, 4);
149 cal.set(Calendar.HOUR_OF_DAY, 12);
150 cal.set(Calendar.MINUTE, 8);
151 cal.set(Calendar.SECOND, 56);
152 cal.set(Calendar.MILLISECOND, 235);
153 return cal;
154 }
155
156 static ArgumentSets testParsesFactory() {
157
158 return ArgumentSets
159 .argumentsForFirstParameter(LONG_FORMAT, SHORT_FORMAT)
160 .argumentsForNextParameter(LocaleUtils.availableLocaleList())
161 .argumentsForNextParameter(NEW_YORK, REYKJAVIK, TimeZones.GMT)
162 .argumentsForNextParameter(2003, 1940, 1868, 1867, 1, -1, -1940);
163
164 }
165
166 private final TriFunction<String, TimeZone, Locale, DateParser> dateParserProvider = (format, timeZone, locale) -> new FastDateParser(format, timeZone,
167 locale, null);
168
169 @BeforeEach
170 @AfterEach
171 void clear() {
172 AbstractFormatCache.clear();
173 FastDateFormat.clear();
174 FastDateParser.clear();
175 FastDatePrinter.clear();
176 }
177
178 private DateParser getDateInstance(final int dateStyle, final Locale locale) {
179 return getInstance(null, AbstractFormatCache.getPatternForStyle(Integer.valueOf(dateStyle), null, locale), TimeZone.getDefault(), Locale.getDefault());
180 }
181
182 private Calendar getEraStart(int year, final TimeZone zone, final Locale locale) {
183 final Calendar cal = Calendar.getInstance(zone, locale);
184 cal.clear();
185
186 if (locale.equals(FastDateParser.JAPANESE_IMPERIAL)) {
187 if (year < 1868) {
188 cal.set(Calendar.ERA, 0);
189 cal.set(Calendar.YEAR, 1868 - year);
190 }
191 } else {
192 if (year < 0) {
193 cal.set(Calendar.ERA, GregorianCalendar.BC);
194 year = -year;
195 }
196 cal.set(Calendar.YEAR, year / 100 * 100);
197 }
198 return cal;
199 }
200
201 DateParser getInstance(final String format) {
202 return getInstance(null, format, TimeZone.getDefault(), Locale.getDefault());
203 }
204
205 DateParser getInstance(final String format, final Locale locale) {
206 return getInstance(null, format, TimeZone.getDefault(), locale);
207 }
208
209 private DateParser getInstance(final String format, final TimeZone timeZone) {
210 return getInstance(null, format, timeZone, Locale.getDefault());
211 }
212
213
214
215
216
217
218
219
220
221
222 protected DateParser getInstance(final TriFunction<String, TimeZone, Locale, DateParser> dpProvider,
223 final String format, final TimeZone timeZone, final Locale locale) {
224 return (dpProvider == null ? this.dateParserProvider : dpProvider).apply(format, timeZone, locale);
225 }
226
227 @ParameterizedTest
228 @MethodSource(DATE_PARSER_PARAMETERS)
229 void test_Equality_Hash(final TriFunction<String, TimeZone, Locale, DateParser> dpProvider) {
230
231 final DateParser[] parsers = {
232 getInstance(dpProvider, yMdHmsSZ, NEW_YORK, Locale.US),
233 getInstance(dpProvider, DMY_DOT, NEW_YORK, Locale.US),
234 getInstance(dpProvider, YMD_SLASH, NEW_YORK, Locale.US),
235 getInstance(dpProvider, MDY_DASH, NEW_YORK, Locale.US),
236 getInstance(dpProvider, MDY_SLASH, NEW_YORK, Locale.US),
237 getInstance(dpProvider, MDY_SLASH, REYKJAVIK, Locale.US),
238 getInstance(dpProvider, MDY_SLASH, REYKJAVIK, SWEDEN)
239 };
240
241 final Map<DateParser, Integer> map = new HashMap<>();
242 int i = 0;
243 for (final DateParser parser : parsers) {
244 map.put(parser, Integer.valueOf(i++));
245 }
246 i = 0;
247 for (final DateParser parser : parsers) {
248 assertEquals(i++, map.get(parser).intValue());
249 }
250 }
251
252 @Test
253 void test1806() throws ParseException {
254 final String formatStub = "yyyy-MM-dd'T'HH:mm:ss.SSS";
255 final String dateStub = "2001-02-04T12:08:56.235";
256
257 for (final Expected1806 trial : Expected1806.values()) {
258 final Calendar cal = initializeCalendar(trial.zone);
259
260 final String message = trial.zone.getDisplayName() + ";";
261
262 DateParser parser = getInstance(formatStub + "X", trial.zone);
263 assertEquals(cal.getTime().getTime(), parser.parse(dateStub + trial.one).getTime() - trial.offset,
264 message + trial.one);
265
266 parser = getInstance(formatStub + "XX", trial.zone);
267 assertEquals(cal.getTime(), parser.parse(dateStub + trial.two), message + trial.two);
268
269 parser = getInstance(formatStub + "XXX", trial.zone);
270 assertEquals(cal.getTime(), parser.parse(dateStub + trial.three), message + trial.three);
271 }
272 }
273
274 @Test
275 void test1806Argument() {
276 assertIllegalArgumentException(() -> getInstance("XXXX"));
277 }
278
279 @ParameterizedTest
280 @MethodSource(DATE_PARSER_PARAMETERS)
281 void testAmPm(final TriFunction<String, TimeZone, Locale, DateParser> dpProvider) throws ParseException {
282 final Calendar cal = Calendar.getInstance(NEW_YORK, Locale.US);
283 cal.clear();
284
285 final DateParser h = getInstance(dpProvider, "yyyy-MM-dd hh a mm:ss", NEW_YORK, Locale.US);
286 final DateParser K = getInstance(dpProvider, "yyyy-MM-dd KK a mm:ss", NEW_YORK, Locale.US);
287 final DateParser k = getInstance(dpProvider, "yyyy-MM-dd kk:mm:ss", NEW_YORK, Locale.US);
288 final DateParser H = getInstance(dpProvider, "yyyy-MM-dd HH:mm:ss", NEW_YORK, Locale.US);
289
290 cal.set(2010, Calendar.AUGUST, 1, 0, 33, 20);
291 assertEquals(cal.getTime(), h.parse("2010-08-01 12 AM 33:20"));
292 assertEquals(cal.getTime(), K.parse("2010-08-01 0 AM 33:20"));
293 assertEquals(cal.getTime(), k.parse("2010-08-01 00:33:20"));
294 assertEquals(cal.getTime(), H.parse("2010-08-01 00:33:20"));
295
296 cal.set(2010, Calendar.AUGUST, 1, 3, 33, 20);
297 assertEquals(cal.getTime(), h.parse("2010-08-01 3 AM 33:20"));
298 assertEquals(cal.getTime(), K.parse("2010-08-01 3 AM 33:20"));
299 assertEquals(cal.getTime(), k.parse("2010-08-01 03:33:20"));
300 assertEquals(cal.getTime(), H.parse("2010-08-01 03:33:20"));
301
302 cal.set(2010, Calendar.AUGUST, 1, 15, 33, 20);
303 assertEquals(cal.getTime(), h.parse("2010-08-01 3 PM 33:20"));
304 assertEquals(cal.getTime(), K.parse("2010-08-01 3 PM 33:20"));
305 assertEquals(cal.getTime(), k.parse("2010-08-01 15:33:20"));
306 assertEquals(cal.getTime(), H.parse("2010-08-01 15:33:20"));
307
308 cal.set(2010, Calendar.AUGUST, 1, 12, 33, 20);
309 assertEquals(cal.getTime(), h.parse("2010-08-01 12 PM 33:20"));
310 assertEquals(cal.getTime(), K.parse("2010-08-01 0 PM 33:20"));
311 assertEquals(cal.getTime(), k.parse("2010-08-01 12:33:20"));
312 assertEquals(cal.getTime(), H.parse("2010-08-01 12:33:20"));
313 }
314
315 @Test
316 void testDayNumberOfWeek() throws ParseException {
317 final DateParser parser = getInstance("u");
318 final Calendar calendar = Calendar.getInstance();
319
320 calendar.setTime(parser.parse("1"));
321 assertEquals(Calendar.MONDAY, calendar.get(Calendar.DAY_OF_WEEK));
322
323 calendar.setTime(parser.parse("6"));
324 assertEquals(Calendar.SATURDAY, calendar.get(Calendar.DAY_OF_WEEK));
325
326 calendar.setTime(parser.parse("7"));
327 assertEquals(Calendar.SUNDAY, calendar.get(Calendar.DAY_OF_WEEK));
328 }
329
330 @ParameterizedTest
331 @MethodSource(DATE_PARSER_PARAMETERS)
332 void testDayOf(final TriFunction<String, TimeZone, Locale, DateParser> dpProvider) throws ParseException {
333 final Calendar cal = Calendar.getInstance(NEW_YORK, Locale.US);
334 cal.clear();
335 cal.set(2003, Calendar.FEBRUARY, 10);
336
337 final DateParser fdf = getInstance(dpProvider, "W w F D y", NEW_YORK, Locale.US);
338 assertEquals(cal.getTime(), fdf.parse("3 7 2 41 03"));
339 }
340
341 @Test
342 void testEquals() {
343 final DateParser parser1 = getInstance(YMD_SLASH);
344 final DateParser parser2 = getInstance(YMD_SLASH);
345
346 assertEquals(parser1, parser2);
347 assertEquals(parser1.hashCode(), parser2.hashCode());
348
349 assertNotEquals(parser1, new Object());
350 }
351
352 @Test
353 void testJpLocales() {
354
355 final Calendar cal = Calendar.getInstance(TimeZones.GMT);
356 cal.clear();
357 cal.set(2003, Calendar.FEBRUARY, 10);
358 cal.set(Calendar.ERA, GregorianCalendar.BC);
359
360 final Locale locale = LocaleUtils.toLocale("zh");
361
362
363 final SimpleDateFormat sdf = new SimpleDateFormat(LONG_FORMAT, locale);
364 final DateParser fdf = getInstance(LONG_FORMAT, locale);
365
366
367 checkParse(locale, cal, sdf, fdf);
368 }
369
370 @ParameterizedTest
371 @MethodSource(DATE_PARSER_PARAMETERS)
372 void testLANG_831(final TriFunction<String, TimeZone, Locale, DateParser> dpProvider) throws Exception {
373 testSdfAndFdp(dpProvider, "M E", "3 Tue", true);
374 }
375
376 @ParameterizedTest
377 @MethodSource(DATE_PARSER_PARAMETERS)
378 void testLANG_832(final TriFunction<String, TimeZone, Locale, DateParser> dpProvider) throws Exception {
379 testSdfAndFdp(dpProvider, "'d'd", "d3", false);
380 testSdfAndFdp(dpProvider, "'d'd'", "d3", true);
381 }
382
383 @ParameterizedTest
384 @MethodSource(DATE_PARSER_PARAMETERS)
385 void testLang1121(final TriFunction<String, TimeZone, Locale, DateParser> dpProvider) throws ParseException {
386 final TimeZone kst = TimeZones.getTimeZone("KST");
387 final DateParser fdp = getInstance(dpProvider, "yyyyMMdd", kst, Locale.KOREA);
388
389 assertThrows(ParseException.class, () -> fdp.parse("2015"));
390
391
392 Date actual = fdp.parse("20150429");
393 final Calendar cal = Calendar.getInstance(kst, Locale.KOREA);
394 cal.clear();
395 cal.set(2015, Calendar.APRIL, 29);
396 Date expected = cal.getTime();
397 assertEquals(expected, actual);
398
399 final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd", Locale.KOREA);
400 sdf.setTimeZone(kst);
401 expected = sdf.parse("20150429113100");
402
403
404 actual = fdp.parse("20150429113100");
405 assertEquals(expected, actual);
406 }
407
408 @ParameterizedTest
409 @MethodSource(DATE_PARSER_PARAMETERS)
410 void testLang1380(final TriFunction<String, TimeZone, Locale, DateParser> dpProvider) throws ParseException {
411 final Calendar expected = Calendar.getInstance(TimeZones.GMT, Locale.FRANCE);
412 expected.clear();
413 expected.set(2014, Calendar.APRIL, 14);
414
415 final DateParser fdp = getInstance(dpProvider, "dd MMM yyyy", TimeZones.GMT, Locale.FRANCE);
416 assertEquals(expected.getTime(), fdp.parse("14 avril 2014"));
417 assertEquals(expected.getTime(), fdp.parse("14 avr. 2014"));
418 assertEquals(expected.getTime(), fdp.parse("14 avr 2014"));
419 }
420
421 @Test
422 void testLang303() throws ParseException {
423 DateParser parser = getInstance(YMD_SLASH);
424 final Calendar cal = Calendar.getInstance();
425 cal.set(2004, Calendar.DECEMBER, 31);
426
427 final Date date = parser.parse("2004/11/31");
428
429 parser = SerializationUtils.deserialize(SerializationUtils.serialize((Serializable) parser));
430 assertEquals(date, parser.parse("2004/11/31"));
431 }
432
433 @Test
434 void testLang538() throws ParseException {
435 final DateParser parser = getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", TimeZones.GMT);
436
437 final Calendar cal = Calendar.getInstance(TimeZones.getTimeZone("GMT-8"));
438 cal.clear();
439 cal.set(2009, Calendar.OCTOBER, 16, 8, 42, 16);
440
441 assertEquals(cal.getTime(), parser.parse("2009-10-16T16:42:16.000Z"));
442 }
443
444 @ParameterizedTest
445 @MethodSource(DATE_PARSER_PARAMETERS)
446 void testLang996(final TriFunction<String, TimeZone, Locale, DateParser> dpProvider) throws ParseException {
447 final Calendar expected = Calendar.getInstance(NEW_YORK, Locale.US);
448 expected.clear();
449 expected.set(2014, Calendar.MAY, 14);
450
451 final DateParser fdp = getInstance(dpProvider, "ddMMMyyyy", NEW_YORK, Locale.US);
452 assertEquals(expected.getTime(), fdp.parse("14may2014"));
453 assertEquals(expected.getTime(), fdp.parse("14MAY2014"));
454 assertEquals(expected.getTime(), fdp.parse("14May2014"));
455 }
456
457 @Test
458 void testLocaleMatches() {
459 final DateParser parser = getInstance(yMdHmsSZ, SWEDEN);
460 assertEquals(SWEDEN, parser.getLocale());
461 }
462
463
464
465
466
467
468 @Test
469 void testLowYearPadding() throws ParseException {
470 final DateParser parser = getInstance(YMD_SLASH);
471 final Calendar cal = Calendar.getInstance();
472 cal.clear();
473
474 cal.set(1, Calendar.JANUARY, 1);
475 assertEquals(cal.getTime(), parser.parse("0001/01/01"));
476 cal.set(10, Calendar.JANUARY, 1);
477 assertEquals(cal.getTime(), parser.parse("0010/01/01"));
478 cal.set(100, Calendar.JANUARY, 1);
479 assertEquals(cal.getTime(), parser.parse("0100/01/01"));
480 cal.set(999, Calendar.JANUARY, 1);
481 assertEquals(cal.getTime(), parser.parse("0999/01/01"));
482 }
483
484 @Test
485 void testMilleniumBug() throws ParseException {
486 final DateParser parser = getInstance(DMY_DOT);
487 final Calendar cal = Calendar.getInstance();
488 cal.clear();
489
490 cal.set(1000, Calendar.JANUARY, 1);
491 assertEquals(cal.getTime(), parser.parse("01.01.1000"));
492 }
493
494 @ParameterizedTest
495 @MethodSource(DATE_PARSER_PARAMETERS)
496 void testParseLongShort(final TriFunction<String, TimeZone, Locale, DateParser> dpProvider)
497 throws ParseException {
498 final Calendar cal = Calendar.getInstance(NEW_YORK, Locale.US);
499 cal.clear();
500 cal.set(2003, Calendar.FEBRUARY, 10, 15, 33, 20);
501 cal.set(Calendar.MILLISECOND, 989);
502 cal.setTimeZone(NEW_YORK);
503
504 DateParser fdf = getInstance(dpProvider, "yyyy GGGG MMMM dddd aaaa EEEE HHHH mmmm ssss SSSS ZZZZ", NEW_YORK,
505 Locale.US);
506
507 assertEquals(cal.getTime(), fdf.parse("2003 AD February 0010 PM Monday 0015 0033 0020 0989 GMT-05:00"));
508 cal.set(Calendar.ERA, GregorianCalendar.BC);
509
510 final Date parse = fdf.parse("2003 BC February 0010 PM Saturday 0015 0033 0020 0989 GMT-05:00");
511 assertEquals(cal.getTime(), parse);
512
513 fdf = getInstance(null, "y G M d a E H m s S Z", NEW_YORK, Locale.US);
514 assertEquals(cal.getTime(), fdf.parse("03 BC 2 10 PM Sat 15 33 20 989 -0500"));
515
516 cal.set(Calendar.ERA, GregorianCalendar.AD);
517 assertEquals(cal.getTime(), fdf.parse("03 AD 2 10 PM Saturday 15 33 20 989 -0500"));
518 }
519
520 @ParameterizedTest
521 @MethodSource(DATE_PARSER_PARAMETERS)
522 void testParseNumerics(final TriFunction<String, TimeZone, Locale, DateParser> dpProvider)
523 throws ParseException {
524 final Calendar cal = Calendar.getInstance(NEW_YORK, Locale.US);
525 cal.clear();
526 cal.set(2003, Calendar.FEBRUARY, 10, 15, 33, 20);
527 cal.set(Calendar.MILLISECOND, 989);
528
529 final DateParser fdf = getInstance(dpProvider, "yyyyMMddHHmmssSSS", NEW_YORK, Locale.US);
530 assertEquals(cal.getTime(), fdf.parse("20030210153320989"));
531 }
532
533 @Test
534 void testParseOffset() {
535 final DateParser parser = getInstance(YMD_SLASH);
536 final Date date = parser.parse("Today is 2015/07/04", new ParsePosition(9));
537
538 final Calendar cal = Calendar.getInstance();
539 cal.clear();
540 cal.set(2015, Calendar.JULY, 4);
541 assertEquals(cal.getTime(), date);
542 }
543
544 @CartesianTest
545 @CartesianTest.MethodFactory("testParsesFactory")
546
547 void testParses(final String format, final Locale locale, final TimeZone timeZone, final int year) throws Exception {
548 final Calendar cal = getEraStart(year, timeZone, locale);
549 final Date centuryStart = cal.getTime();
550 cal.set(Calendar.MONTH, 1);
551 cal.set(Calendar.DAY_OF_MONTH, 10);
552 final Date in = cal.getTime();
553 final FastDateParser fastDateParser = new FastDateParser(format, timeZone, locale, centuryStart);
554 validateSdfFormatFdpParseEquality(format, locale, timeZone, fastDateParser, in, year, centuryStart);
555 }
556
557
558
559
560 @Test
561 void testParsesKnownJava16Ea25Failure() throws Exception {
562 final String format = LONG_FORMAT;
563 final int year = 2003;
564 final Locale locale = new Locale.Builder().setLanguage("sq").setRegion("MK").build();
565 assertEquals("sq_MK", locale.toString());
566 assertNotNull(locale);
567 final TimeZone timeZone = NEW_YORK;
568 final Calendar cal = getEraStart(year, timeZone, locale);
569 final Date centuryStart = cal.getTime();
570
571 cal.set(Calendar.MONTH, 1);
572 cal.set(Calendar.DAY_OF_MONTH, 10);
573 final Date in = cal.getTime();
574
575 final FastDateParser fastDateParser = new FastDateParser(format, timeZone, locale, centuryStart);
576 validateSdfFormatFdpParseEquality(format, locale, timeZone, fastDateParser, in, year, centuryStart);
577 }
578
579 @ParameterizedTest
580 @MethodSource(DATE_PARSER_PARAMETERS)
581 void testParseZone(final TriFunction<String, TimeZone, Locale, DateParser> dpProvider)
582 throws ParseException {
583 final Calendar cal = Calendar.getInstance(NEW_YORK, Locale.US);
584 cal.clear();
585 cal.set(2003, Calendar.JULY, 10, 16, 33, 20);
586
587 final DateParser fdf = getInstance(dpProvider, yMdHmsSZ, NEW_YORK, Locale.US);
588
589 assertEquals(cal.getTime(), fdf.parse("2003-07-10T15:33:20.000 -0500"));
590 assertEquals(cal.getTime(), fdf.parse("2003-07-10T15:33:20.000 GMT-05:00"));
591 assertEquals(cal.getTime(), fdf.parse("2003-07-10T16:33:20.000 Eastern Daylight Time"));
592 assertEquals(cal.getTime(), fdf.parse("2003-07-10T16:33:20.000 EDT"));
593
594 cal.setTimeZone(TimeZones.getTimeZone("GMT-3"));
595 cal.set(2003, Calendar.FEBRUARY, 10, 9, 0, 0);
596
597 assertEquals(cal.getTime(), fdf.parse("2003-02-10T09:00:00.000 -0300"));
598
599 cal.setTimeZone(TimeZones.getTimeZone("GMT+5"));
600 cal.set(2003, Calendar.FEBRUARY, 10, 15, 5, 6);
601
602 assertEquals(cal.getTime(), fdf.parse("2003-02-10T15:05:06.000 +0500"));
603 }
604
605 @Test
606 void testPatternMatches() {
607 final DateParser parser = getInstance(yMdHmsSZ);
608 assertEquals(yMdHmsSZ, parser.getPattern());
609 }
610
611 @ParameterizedTest
612 @MethodSource(DATE_PARSER_PARAMETERS)
613 void testQuotes(final TriFunction<String, TimeZone, Locale, DateParser> dpProvider) throws ParseException {
614 final Calendar cal = Calendar.getInstance(NEW_YORK, Locale.US);
615 cal.clear();
616 cal.set(2003, Calendar.FEBRUARY, 10, 15, 33, 20);
617 cal.set(Calendar.MILLISECOND, 989);
618
619 final DateParser fdf = getInstance(dpProvider, "''yyyyMMdd'A''B'HHmmssSSS''", NEW_YORK, Locale.US);
620 assertEquals(cal.getTime(), fdf.parse("'20030210A'B153320989'"));
621 }
622
623 private void testSdfAndFdp(final TriFunction<String, TimeZone, Locale, DateParser> dbProvider, final String format,
624 final String date, final boolean shouldFail) throws Exception {
625 Date dfdp = null;
626 Date dsdf = null;
627 Throwable f = null;
628 Throwable s = null;
629
630 try {
631 final SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.US);
632 sdf.setTimeZone(NEW_YORK);
633 dsdf = sdf.parse(date);
634 assertFalse(shouldFail, "Expected SDF failure, but got " + dsdf + " for [" + format + ", " + date + "]");
635 } catch (final Exception e) {
636 s = e;
637 if (!shouldFail) {
638 throw e;
639 }
640 }
641
642 try {
643 final DateParser fdp = getInstance(dbProvider, format, NEW_YORK, Locale.US);
644 dfdp = fdp.parse(date);
645 assertFalse(shouldFail, "Expected FDF failure, but got " + dfdp + " for [" + format + ", " + date + "]");
646 } catch (final Exception e) {
647 f = e;
648 if (!shouldFail) {
649 throw e;
650 }
651 }
652
653 assertEquals(f == null, s == null, "Should both or neither throw Exceptions");
654 assertEquals(dsdf, dfdp, "Parsed dates should be equal");
655 }
656
657
658
659
660
661
662 @Test
663 void testShortDateStyleWithLocales() throws ParseException {
664 DateParser fdf = getDateInstance(FastDateFormat.SHORT, Locale.US);
665 final Calendar cal = Calendar.getInstance();
666 cal.clear();
667
668 cal.set(2004, Calendar.FEBRUARY, 3);
669 assertEquals(cal.getTime(), fdf.parse("2/3/04"));
670
671 fdf = getDateInstance(FastDateFormat.SHORT, SWEDEN);
672 assertEquals(cal.getTime(), fdf.parse("2004-02-03"));
673 }
674
675 @ParameterizedTest
676 @MethodSource(DATE_PARSER_PARAMETERS)
677 void testSpecialCharacters(final TriFunction<String, TimeZone, Locale, DateParser> dpProvider)
678 throws Exception {
679 testSdfAndFdp(dpProvider, "q", "", true);
680 testSdfAndFdp(dpProvider, "Q", "", true);
681 testSdfAndFdp(dpProvider, "$", "$", false);
682 testSdfAndFdp(dpProvider, "?.d", "?.12", false);
683 testSdfAndFdp(dpProvider, "''yyyyMMdd'A''B'HHmmssSSS''", "'20030210A'B153320989'", false);
684 testSdfAndFdp(dpProvider, "''''yyyyMMdd'A''B'HHmmssSSS''", "''20030210A'B153320989'", false);
685 testSdfAndFdp(dpProvider, "'$\\Ed'", "$\\Ed", false);
686
687
688 testSdfAndFdp(dpProvider, "'QED'", "QED", false);
689 testSdfAndFdp(dpProvider, "'QED'", "qed", true);
690
691 testSdfAndFdp(dpProvider, "yyyy-MM-dd 'QED'", "2003-02-10 QED", false);
692 testSdfAndFdp(dpProvider, "yyyy-MM-dd 'QED'", "2003-02-10 qed", true);
693 }
694
695 @Test
696 void testTimeZoneMatches() {
697 final DateParser parser = getInstance(yMdHmsSZ, REYKJAVIK);
698 assertEquals(REYKJAVIK, parser.getTimeZone());
699 }
700
701 @Test
702 void testToStringContainsName() {
703 final DateParser parser = getInstance(YMD_SLASH);
704 assertTrue(parser.toString().startsWith("FastDate"));
705 }
706
707
708
709 @ParameterizedTest
710 @MethodSource("org.apache.commons.lang3.LocaleUtils#availableLocaleList()")
711 void testTzParses(final Locale locale) throws Exception {
712
713 final FastDateParser fdp = new FastDateParser("yyyy/MM/dd z", TimeZone.getDefault(), locale);
714 for (final TimeZone timeZone : new TimeZone[] { NEW_YORK, REYKJAVIK, TimeZones.GMT }) {
715 final Calendar cal = Calendar.getInstance(timeZone, locale);
716 cal.clear();
717 cal.set(Calendar.YEAR, 2000);
718 cal.set(Calendar.MONTH, 1);
719 cal.set(Calendar.DAY_OF_MONTH, 10);
720 final Date expected = cal.getTime();
721 final Date actual = fdp.parse("2000/02/10 " + timeZone.getDisplayName(locale));
722 assertEquals(expected, actual, "timeZone:" + timeZone.getID() + " locale:" + locale.getDisplayName());
723 }
724 }
725
726 private void validateSdfFormatFdpParseEquality(final String formatStr, final Locale locale, final TimeZone timeZone,
727 final FastDateParser dateParser, final Date inDate, final int year, final Date csDate) throws ParseException {
728 final SimpleDateFormat sdf = new SimpleDateFormat(formatStr, locale);
729 sdf.setTimeZone(timeZone);
730 if (formatStr.equals(SHORT_FORMAT)) {
731 sdf.set2DigitYearStart(csDate);
732 }
733 final String fmt = sdf.format(inDate);
734
735
736 try {
737 final Date out = dateParser.parse(fmt);
738 assertEquals(inDate, out, "format: '" + formatStr + "', locale: '" + locale + "', time zone: '"
739 + timeZone.getID() + "', year: " + year + ", parse: '" + fmt);
740 } catch (final ParseException pe) {
741 if (year >= 1868 || !locale.getCountry().equals("JP")) {
742
743 throw pe;
744 }
745 }
746 }
747 }
748