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