1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.opencsv.bean;
17
18 import com.opencsv.ICSVParser;
19 import com.opencsv.exceptions.CsvBadConverterException;
20 import com.opencsv.exceptions.CsvDataTypeMismatchException;
21 import org.apache.commons.lang3.StringUtils;
22
23 import javax.xml.datatype.DatatypeConfigurationException;
24 import javax.xml.datatype.DatatypeFactory;
25 import javax.xml.datatype.XMLGregorianCalendar;
26 import java.lang.reflect.InvocationTargetException;
27 import java.text.ParseException;
28 import java.text.SimpleDateFormat;
29 import java.time.*;
30 import java.time.chrono.*;
31 import java.time.format.DateTimeFormatter;
32 import java.time.temporal.ChronoField;
33 import java.time.temporal.Temporal;
34 import java.time.temporal.TemporalAccessor;
35 import java.util.*;
36 import java.util.function.BiFunction;
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 public class ConverterDate extends AbstractCsvConverter {
65
66 private static final String CSVDATE_NOT_DATE = "csvdate.not.date";
67
68
69
70
71
72
73 private final SimpleDateFormat readSdf;
74
75
76
77
78
79
80 private final SimpleDateFormat writeSdf;
81
82
83
84
85
86 private final DateTimeFormatter readDtf;
87
88
89
90
91
92 private final DateTimeFormatter writeDtf;
93
94
95
96
97
98 private final BiFunction<DateTimeFormatter, String, TemporalAccessor> readTemporalConversionFunction;
99
100
101
102
103
104 private final BiFunction<DateTimeFormatter, TemporalAccessor, String> writeTemporalConversionFunction;
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 public ConverterDate(Class<?> type, String locale, String writeLocale, Locale errorLocale, String readFormat, String writeFormat, String readChronology, String writeChronology) {
132 super(type, locale, writeLocale, errorLocale);
133
134
135 Chronology readChrono = getChronology(readChronology, this.locale);
136 Chronology writeChrono = getChronology(writeChronology, this.writeLocale);
137
138
139 try {
140 if (TemporalAccessor.class.isAssignableFrom(type)) {
141 readSdf = null;
142 DateTimeFormatter dtfWithoutChronology = setDateTimeFormatter(readFormat, this.locale);
143 readDtf = dtfWithoutChronology.withChronology(readChrono);
144
145 readTemporalConversionFunction = determineReadTemporalConversionFunction(type);
146
147 } else {
148 readDtf = null;
149 readTemporalConversionFunction = null;
150 readSdf = setDateFormat(readFormat, this.locale);
151 }
152 } catch (IllegalArgumentException e) {
153 CsvBadConverterException csve = new CsvBadConverterException(getClass(), String.format(
154 ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, this.errorLocale)
155 .getString("invalid.date.format.string"), readFormat));
156 csve.initCause(e);
157 throw csve;
158 }
159
160
161 try {
162 if (TemporalAccessor.class.isAssignableFrom(type)) {
163 writeSdf = null;
164 DateTimeFormatter dtfWithoutChronology = setDateTimeFormatter(writeFormat, this.writeLocale);
165 writeDtf = dtfWithoutChronology.withChronology(writeChrono);
166 writeTemporalConversionFunction = determineWriteTemporalConversionFunction(type);
167 } else {
168 writeDtf = null;
169 writeTemporalConversionFunction = null;
170 writeSdf = setDateFormat(writeFormat, this.writeLocale);
171 }
172 } catch (IllegalArgumentException e) {
173 CsvBadConverterException csve = new CsvBadConverterException(getClass(), String.format(
174 ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, this.errorLocale)
175 .getString("invalid.date.format.string"), writeFormat));
176 csve.initCause(e);
177 throw csve;
178 }
179 }
180
181 private BiFunction<DateTimeFormatter, TemporalAccessor, String> determineWriteTemporalConversionFunction(Class<?> type) {
182 if (Instant.class.equals(type)) {
183 return (writeDtf, value) -> {
184 LocalDateTime ldt = LocalDateTime.ofInstant((Instant) value, ZoneId.of("UTC"));
185 return writeDtf.format(ldt);
186 };
187 } else {
188 return (writeDtf, value) -> writeDtf.format((TemporalAccessor) value);
189 }
190 }
191
192 private BiFunction<DateTimeFormatter, String, TemporalAccessor> determineReadTemporalConversionFunction(Class<?> type) {
193
194 if (TemporalAccessor.class.equals(type)) {
195 return DateTimeFormatter::parse;
196 } else if (ChronoLocalDateTime.class.equals(type)
197 || LocalDateTime.class.equals(type)) {
198 return (readDtf, s) -> readDtf.parse(s, LocalDateTime::from);
199 } else if (ChronoZonedDateTime.class.equals(type)
200 || ZonedDateTime.class.equals(type)) {
201 return (readDtf, s) -> readDtf.parse(s, ZonedDateTime::from);
202 } else if (Temporal.class.equals(type)) {
203 return (readDtf, s) -> readDtf.parseBest(s, ZonedDateTime::from,
204 OffsetDateTime::from, Instant::from,
205 LocalDateTime::from, LocalDate::from, OffsetTime::from,
206 LocalTime::from);
207 } else if (Era.class.equals(type) || IsoEra.class.equals(type)) {
208 return (readDtf, s) -> IsoEra.of(readDtf.parse(s).get(ChronoField.ERA));
209 } else if (DayOfWeek.class.equals(type)) {
210 return (readDtf, s) -> readDtf.parse(s, DayOfWeek::from);
211 } else if (HijrahEra.class.equals(type)) {
212 return (readDtf, s) -> HijrahEra.of(readDtf.parse(s).get(ChronoField.ERA));
213 } else if (Instant.class.equals(type)) {
214 return (readDtf, s) -> readDtf.parse(s, Instant::from);
215 } else if (ChronoLocalDate.class.isAssignableFrom(type)) {
216 return (readDtf, s) -> readDtf.parse(s, ChronoLocalDate::from);
217 } else if (JapaneseEra.class.equals(type)) {
218 return (readDtf, s) -> JapaneseEra.of(readDtf.parse(s).get(ChronoField.ERA));
219 } else if (LocalTime.class.equals(type)) {
220 return (readDtf, s) -> readDtf.parse(s, LocalTime::from);
221 } else if (MinguoEra.class.equals(type)) {
222 return (readDtf, s) -> MinguoEra.of(readDtf.parse(s).get(ChronoField.ERA));
223 } else if (Month.class.equals(type)) {
224 return (readDtf, s) -> readDtf.parse(s, Month::from);
225 } else if (MonthDay.class.equals(type)) {
226 return (readDtf, s) -> readDtf.parse(s, MonthDay::from);
227 } else if (OffsetDateTime.class.equals(type)) {
228 return (readDtf, s) -> readDtf.parse(s, OffsetDateTime::from);
229 } else if (OffsetTime.class.equals(type)) {
230 return (readDtf, s) -> readDtf.parse(s, OffsetTime::from);
231 } else if (ThaiBuddhistEra.class.equals(type)) {
232 return (readDtf, s) -> ThaiBuddhistEra.of(readDtf.parse(s).get(ChronoField.ERA));
233 } else if (Year.class.equals(type)) {
234 return (readDtf, s) -> readDtf.parse(s, Year::from);
235 } else if (YearMonth.class.equals(type)) {
236 return (readDtf, s) -> readDtf.parse(s, YearMonth::from);
237 } else if (ZoneOffset.class.equals(type)) {
238 return (readDtf, s) -> readDtf.parse(s, ZoneOffset::from);
239 } else {
240 throw new CsvBadConverterException(getClass(), String.format(
241 ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, this.errorLocale)
242 .getString(CSVDATE_NOT_DATE), type));
243 }
244 }
245
246 private SimpleDateFormat setDateFormat(String format, Locale formatLocale) {
247 if (formatLocale != null) {
248 return new SimpleDateFormat(format, formatLocale);
249 }
250 return new SimpleDateFormat(format);
251 }
252
253 private DateTimeFormatter setDateTimeFormatter(String format, Locale formatLocale) {
254 if (this.writeLocale != null) {
255 return DateTimeFormatter.ofPattern(format, formatLocale);
256 }
257 return DateTimeFormatter.ofPattern(format);
258 }
259
260 private Chronology getChronology(String readChronology, Locale locale2) {
261 Chronology readChrono;
262 try {
263 readChrono = StringUtils.isNotBlank(readChronology) ?
264 Chronology.of(readChronology) :
265 Chronology.ofLocale(locale2);
266 } catch (DateTimeException e) {
267 CsvBadConverterException csve = new CsvBadConverterException(getClass(),
268 String.format(ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, this.errorLocale)
269 .getString("chronology.not.found"), readChronology));
270 csve.initCause(e);
271 throw csve;
272 }
273 return readChrono;
274 }
275
276 @Override
277 public Object convertToRead(String value) throws CsvDataTypeMismatchException {
278 Object returnValue = null;
279 if (StringUtils.isNotBlank(value)) {
280
281
282 if (Date.class.isAssignableFrom(type)) {
283 Date d;
284 try {
285 synchronized (readSdf) {
286 d = readSdf.parse(value);
287 }
288
289 returnValue = type.getConstructor(Long.TYPE).newInstance(d.getTime());
290 }
291
292
293
294
295
296 catch (ParseException | InstantiationException
297 | IllegalAccessException | NoSuchMethodException
298 | InvocationTargetException e) {
299 CsvDataTypeMismatchException csve = new CsvDataTypeMismatchException(value, type);
300 csve.initCause(e);
301 throw csve;
302 }
303
304 } else if (TemporalAccessor.class.isAssignableFrom(type)) {
305 try {
306 returnValue = type.cast(readTemporalConversionFunction.apply(readDtf, value));
307 } catch (DateTimeException | ArithmeticException e) {
308 CsvDataTypeMismatchException csve = new CsvDataTypeMismatchException(value, type);
309 csve.initCause(e);
310 throw csve;
311 }
312
313 } else if (Calendar.class.isAssignableFrom(type)
314 || XMLGregorianCalendar.class.isAssignableFrom(type)) {
315
316 Date d;
317 try {
318 synchronized (readSdf) {
319 d = readSdf.parse(value);
320 }
321 } catch (ParseException e) {
322 CsvDataTypeMismatchException csve = new CsvDataTypeMismatchException(value, type);
323 csve.initCause(e);
324 throw csve;
325 }
326
327
328
329 GregorianCalendar gc = new GregorianCalendar();
330 gc.setTime(d);
331
332
333 if (type == XMLGregorianCalendar.class) {
334 try {
335 returnValue = type.cast(DatatypeFactory
336 .newInstance()
337 .newXMLGregorianCalendar(gc));
338 } catch (DatatypeConfigurationException e) {
339
340
341
342 CsvDataTypeMismatchException ex = new CsvDataTypeMismatchException(
343 ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale)
344 .getString("xmlgregoriancalendar.impossible"));
345 ex.initCause(e);
346 throw ex;
347 }
348 } else {
349 returnValue = type.cast(gc);
350 }
351 } else {
352 throw new CsvDataTypeMismatchException(value, type, String.format(
353 ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale).getString(CSVDATE_NOT_DATE), type));
354 }
355 }
356
357 return returnValue;
358 }
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373 @Override
374 public String convertToWrite(Object value)
375 throws CsvDataTypeMismatchException {
376 String returnValue = null;
377 if (value != null) {
378
379
380 if (Date.class.isAssignableFrom(type)) {
381 synchronized (writeSdf) {
382 returnValue = writeSdf.format((Date) value);
383 }
384
385 } else if (TemporalAccessor.class.isAssignableFrom(type)) {
386 try {
387 returnValue = writeTemporalConversionFunction.apply(writeDtf, (TemporalAccessor) value);
388 } catch (DateTimeException | ArithmeticException e) {
389 CsvDataTypeMismatchException csve = new CsvDataTypeMismatchException(value, type);
390 csve.initCause(e);
391 throw csve;
392 }
393
394 } else if (Calendar.class.isAssignableFrom(type)
395 || XMLGregorianCalendar.class.isAssignableFrom(type)) {
396 Calendar c;
397 if (value instanceof XMLGregorianCalendar) {
398 c = ((XMLGregorianCalendar) value).toGregorianCalendar();
399 } else {
400 c = (Calendar) value;
401 }
402 synchronized (writeSdf) {
403 returnValue = writeSdf.format(c.getTime());
404 }
405 } else {
406 throw new CsvDataTypeMismatchException(value, type, String.format(
407 ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale).getString(CSVDATE_NOT_DATE), type));
408 }
409 }
410 return returnValue;
411 }
412 }