1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.beanutils2.converters;
19
20 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
21 import static org.junit.jupiter.api.Assertions.assertEquals;
22 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
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.text.DateFormat;
29 import java.text.SimpleDateFormat;
30 import java.time.Instant;
31 import java.time.LocalDate;
32 import java.time.LocalDateTime;
33 import java.time.OffsetDateTime;
34 import java.time.ZoneId;
35 import java.time.ZonedDateTime;
36 import java.util.Calendar;
37 import java.util.Date;
38 import java.util.GregorianCalendar;
39 import java.util.Locale;
40 import java.util.Objects;
41
42 import org.apache.commons.beanutils2.ConversionException;
43 import org.apache.commons.beanutils2.Converter;
44 import org.junit.jupiter.api.Test;
45
46
47
48
49
50
51 public abstract class AbstractDateConverterTest<T> {
52
53
54
55
56
57
58 protected abstract Class<T> getExpectedType();
59
60
61
62
63
64
65
66 protected long getTimeInMillis(final Object date) {
67 if (date instanceof java.sql.Date) {
68 return ((java.sql.Date) date).getTime();
69 }
70
71 if (date instanceof java.sql.Timestamp) {
72 return ((java.sql.Timestamp) date).getTime();
73 }
74
75 if (date instanceof LocalDate) {
76 return ((LocalDate) date).atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
77 }
78
79 if (date instanceof LocalDateTime) {
80 return ((LocalDateTime) date).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
81 }
82
83 if (date instanceof ZonedDateTime) {
84 return ((ZonedDateTime) date).toInstant().toEpochMilli();
85 }
86
87 if (date instanceof OffsetDateTime) {
88 return ((OffsetDateTime) date).toInstant().toEpochMilli();
89 }
90
91 if (date instanceof Calendar) {
92 return ((Calendar) date).getTime().getTime();
93 }
94
95 if (date instanceof Date) {
96 return ((Date) date).getTime();
97 }
98 throw new IllegalArgumentException(Objects.toString(date));
99 }
100
101
102
103
104
105
106
107 protected void invalidConversion(final Converter<T> converter, final Object value) {
108 final String valueType = value == null ? "null" : value.getClass().getName();
109 final String msg = "Converting '" + valueType + "' value '" + value + "'";
110 try {
111 final T result = converter.convert(getExpectedType(), value);
112 fail(msg + ", expected ConversionException, but result = '" + result + "'");
113 } catch (final ConversionException ex) {
114
115 }
116 }
117
118
119
120
121
122
123 protected abstract DateTimeConverter<T> makeConverter();
124
125
126
127
128
129
130
131 protected abstract DateTimeConverter<T> makeConverter(T defaultValue);
132
133
134
135
136
137
138
139
140 protected void stringConversion(final Converter<T> converter, final String expected, final Object value) {
141 final String valueType = value == null ? "null" : value.getClass().getName();
142 final String msg = "Converting '" + valueType + "' value '" + value + "' to String";
143 try {
144 final String result = converter.convert(String.class, value);
145 final Class<?> resultType = result == null ? null : result.getClass();
146 final Class<?> expectType = expected == null ? null : expected.getClass();
147 assertEquals(expectType, resultType, () -> "TYPE " + msg);
148 assertEquals(expected, result, () -> "VALUE " + msg);
149 } catch (final Exception ex) {
150 throw new IllegalStateException(msg + " threw " + ex.toString(), ex);
151 }
152 }
153
154
155
156
157 @Test
158 public void testConvertDate() {
159 final String[] message = { "from Date", "from Calendar", "from SQL Date", "from SQL Time", "from SQL Timestamp", "from LocalDate", "from LocalDateTime",
160 "from ZonedDateTime", "from OffsetDateTime" };
161
162 final long nowMillis = System.currentTimeMillis();
163
164 final Object[] date = { new Date(nowMillis), new java.util.GregorianCalendar(), new java.sql.Date(nowMillis), new java.sql.Time(nowMillis),
165 new java.sql.Timestamp(nowMillis),
166 Instant.ofEpochMilli(nowMillis).atZone(ZoneId.systemDefault()).toLocalDate().atStartOfDay(ZoneId.systemDefault()).toLocalDate(),
167 Instant.ofEpochMilli(nowMillis).atZone(ZoneId.systemDefault()).toLocalDateTime(),
168 ZonedDateTime.ofInstant(Instant.ofEpochMilli(nowMillis), ZoneId.systemDefault()),
169 OffsetDateTime.ofInstant(Instant.ofEpochMilli(nowMillis), ZoneId.systemDefault()) };
170
171
172 ((GregorianCalendar) date[1]).setTime(new Date(nowMillis));
173
174 for (int i = 0; i < date.length; i++) {
175 final Class<T> expectedType = getExpectedType();
176 final Object val = makeConverter().convert(expectedType, date[i]);
177 assertNotNull(val, "Convert " + message[i] + " should not be null");
178 assertInstanceOf(expectedType, val, "Convert " + message[i] + " should return a " + expectedType.getName());
179
180 long test = nowMillis;
181 if (date[i] instanceof LocalDate || val instanceof LocalDate) {
182 test = Instant.ofEpochMilli(nowMillis).atZone(ZoneId.systemDefault()).toLocalDate().atStartOfDay(ZoneId.systemDefault()).toInstant()
183 .toEpochMilli();
184 }
185
186 assertEquals(test, getTimeInMillis(val), "Convert " + message[i] + " should return a " + date[0]);
187 }
188 }
189
190
191
192
193 @Test
194 public void testConvertNull() {
195 assertThrows(ConversionException.class,
196 () -> makeConverter().convert(getExpectedType(), null),
197 "Expected ConversionException");
198 }
199
200
201
202
203
204
205 @Test
206 public void testDefaultStringToTypeConvert() {
207
208
209 final DateTimeConverter<T> converter = makeConverter();
210 converter.setUseLocaleFormat(false);
211 assertThrows(ConversionException.class,
212 () -> converter.convert(getExpectedType(), "2006-10-23"),
213 "Expected Conversion exception");
214
215 }
216
217
218
219
220 @Test
221 public void testDefaultType() {
222 final String pattern = "yyyy-MM-dd";
223
224
225 final DateTimeConverter<T> converter = makeConverter();
226 converter.setPattern(pattern);
227
228
229 final String testString = "2006-10-29";
230 final Calendar calendar = toCalendar(testString, pattern, null);
231 final Object expected = toType(calendar);
232
233 final Object result = converter.convert(null, testString);
234 final Class<T> expectedType = getExpectedType();
235 if (expectedType.equals(Calendar.class)) {
236 assertTrue(expectedType.isAssignableFrom(result.getClass()), "TYPE ");
237 } else {
238 assertInstanceOf(expectedType, result, "TYPE ");
239 }
240 assertEquals(expected, result, "VALUE ");
241 }
242
243
244
245
246 @Test
247 public void testInvalidType() {
248
249
250 @SuppressWarnings("unchecked")
251 final DateTimeConverter<Character> converter = (DateTimeConverter<Character>) makeConverter();
252
253
254 assertThrows(ConversionException.class,
255 ()-> converter.convert(Character.class, new Date()),
256 "Requested Character.class conversion, expected ConversionException");
257 }
258
259
260
261
262 @Test
263 public void testLocale() {
264
265
266 final Locale defaultLocale = Locale.getDefault();
267 Locale.setDefault(Locale.US);
268
269 final String pattern = "M/d/yy";
270
271
272 final DateTimeConverter<T> converter = makeConverter();
273 converter.setUseLocaleFormat(true);
274
275
276 final String testString = "10/28/06";
277 final Object expected = toType(testString, pattern, null);
278 validConversion(converter, expected, testString);
279
280
281 invalidConversion(converter, null);
282 invalidConversion(converter, "");
283 invalidConversion(converter, "2006-10-2X");
284 invalidConversion(converter, "10.28.06");
285 invalidConversion(converter, "10-28-06");
286 invalidConversion(converter, Integer.valueOf(2));
287
288
289 Locale.setDefault(defaultLocale);
290
291 }
292
293
294
295
296 @Test
297 public void testMultiplePatterns() {
298 String testString;
299 Object expected;
300
301
302 final String[] patterns = { "yyyy-MM-dd", "yyyy/MM/dd" };
303 final DateTimeConverter<T> converter = makeConverter();
304 converter.setPatterns(patterns);
305
306
307 testString = "2006-10-28";
308 expected = toType(testString, patterns[0], null);
309 validConversion(converter, expected, testString);
310
311
312 testString = "2006/10/18";
313 expected = toType(testString, patterns[1], null);
314 validConversion(converter, expected, testString);
315
316
317 invalidConversion(converter, "17/03/2006");
318 invalidConversion(converter, "17.03.2006");
319
320 }
321
322
323
324
325 @Test
326 public void testPatternDefault() {
327
328 final String pattern = "yyyy-MM-dd";
329
330
331 final T defaultValue = toType("2000-01-01", pattern, null);
332 assertNotNull(defaultValue, "Check default date");
333 final DateTimeConverter<T> converter = makeConverter(defaultValue);
334 converter.setPattern(pattern);
335
336
337 final String testString = "2006-10-29";
338 final Object expected = toType(testString, pattern, null);
339 validConversion(converter, expected, testString);
340
341
342 validConversion(converter, defaultValue, null);
343 validConversion(converter, defaultValue, "");
344 validConversion(converter, defaultValue, "2006-10-2X");
345 validConversion(converter, defaultValue, "2006/10/01");
346 validConversion(converter, defaultValue, "02/10/06");
347 validConversion(converter, defaultValue, Integer.valueOf(2));
348
349 }
350
351
352
353
354 @Test
355 public void testPatternNoDefault() {
356
357 final String pattern = "yyyy-MM-dd";
358
359
360 final DateTimeConverter<T> converter = makeConverter();
361 converter.setPattern(pattern);
362
363
364 final String testString = "2006-10-29";
365 final Calendar calendar = toCalendar(testString, pattern, null);
366 final Object expected = toType(calendar);
367 validConversion(converter, expected, testString);
368
369
370 validConversion(converter, expected, calendar);
371
372
373 validConversion(converter, expected, toDate(calendar));
374
375
376 validConversion(converter, expected, toSqlDate(calendar));
377
378
379 validConversion(converter, expected, toSqlTimestamp(calendar));
380
381
382 validConversion(converter, expected, toSqlTime(calendar));
383
384
385 invalidConversion(converter, null);
386 invalidConversion(converter, "");
387 invalidConversion(converter, "2006-10-2X");
388 invalidConversion(converter, "2006/10/01");
389 invalidConversion(converter, "02/10/2006");
390 invalidConversion(converter, "02/10/06");
391 invalidConversion(converter, Integer.valueOf(2));
392
393 }
394
395
396
397
398 @Test
399 public void testPatternNullDefault() {
400
401 final String pattern = "yyyy-MM-dd";
402
403
404 final T defaultValue = null;
405 final DateTimeConverter<T> converter = makeConverter(defaultValue);
406 converter.setPattern(pattern);
407
408
409 final String testString = "2006-10-29";
410 final Object expected = toType(testString, pattern, null);
411 validConversion(converter, expected, testString);
412
413
414 validConversion(converter, defaultValue, null);
415 validConversion(converter, defaultValue, "");
416 validConversion(converter, defaultValue, "2006-10-2X");
417 validConversion(converter, defaultValue, "2006/10/01");
418 validConversion(converter, defaultValue, "02/10/06");
419 validConversion(converter, defaultValue, Integer.valueOf(2));
420
421 }
422
423
424
425
426 @Test
427 public void testStringConversion() {
428
429 final String pattern = "yyyy-MM-dd";
430
431
432 final DateTimeConverter<T> converter = makeConverter();
433 converter.setPattern(pattern);
434
435
436 final String expected = "2006-10-29";
437 final Calendar calendar = toCalendar(expected, pattern, null);
438
439
440 stringConversion(converter, expected, toType(calendar));
441
442
443 stringConversion(converter, expected, calendar);
444
445
446 stringConversion(converter, expected, toDate(calendar));
447
448
449 stringConversion(converter, expected, toSqlDate(calendar));
450
451
452 stringConversion(converter, expected, toSqlTimestamp(calendar));
453
454
455 stringConversion(converter, expected, toSqlTime(calendar));
456
457
458 stringConversion(converter, expected, toLocalDateTime(calendar));
459
460 stringConversion(converter, null, null);
461 stringConversion(converter, "", "");
462
463 }
464
465
466
467
468
469
470
471
472
473 Calendar toCalendar(final String value, final String pattern, final Locale locale) {
474 Calendar calendar = null;
475 try {
476 final DateFormat format = locale == null ? new SimpleDateFormat(pattern) : new SimpleDateFormat(pattern, locale);
477 format.setLenient(false);
478 format.parse(value);
479 calendar = format.getCalendar();
480 } catch (final Exception e) {
481 fail("Error creating Calendar value ='" + value + ", pattern='" + pattern + "' " + e.toString());
482 }
483 return calendar;
484 }
485
486
487
488
489
490
491
492 Date toDate(final Calendar calendar) {
493 return calendar.getTime();
494 }
495
496
497
498
499
500
501
502 LocalDateTime toLocalDateTime(final Calendar calendar) {
503 return Instant.ofEpochMilli(calendar.getTimeInMillis()).atZone(ZoneId.systemDefault()).toLocalDateTime();
504 }
505
506
507
508
509
510
511
512 java.sql.Date toSqlDate(final Calendar calendar) {
513 return new java.sql.Date(getTimeInMillis(calendar));
514 }
515
516
517
518
519
520
521
522 java.sql.Time toSqlTime(final Calendar calendar) {
523 return new java.sql.Time(getTimeInMillis(calendar));
524 }
525
526
527
528
529
530
531
532 java.sql.Timestamp toSqlTimestamp(final Calendar calendar) {
533 return new java.sql.Timestamp(getTimeInMillis(calendar));
534 }
535
536
537
538
539
540
541
542 protected abstract T toType(Calendar value);
543
544
545
546
547
548
549
550
551
552 protected T toType(final String value, final String pattern, final Locale locale) {
553 return toType(toCalendar(value, pattern, locale));
554 }
555
556
557
558
559
560
561
562
563 protected void validConversion(final Converter<T> converter, final Object expected, final Object value) {
564 final String valueType = value == null ? "null" : value.getClass().getName();
565 final String msg = "Converting '" + valueType + "' value '" + value + "'";
566 final Object result = assertDoesNotThrow(() -> converter.convert(getExpectedType(), value));
567 final Class<?> resultType = result == null ? null : result.getClass();
568 final Class<?> expectType = expected == null ? null : expected.getClass();
569 assertEquals(expectType, resultType, () -> "TYPE " + msg);
570 assertEquals(expected, result, () -> "VALUE " + msg);
571 }
572 }