|
24 | 24 | import java.io.IOException; |
25 | 25 | import java.text.ParseException; |
26 | 26 | import java.text.SimpleDateFormat; |
| 27 | +import java.time.LocalDateTime; |
| 28 | +import java.time.ZoneId; |
27 | 29 | import java.time.ZonedDateTime; |
28 | 30 | import java.time.format.DateTimeFormatter; |
| 31 | +import java.time.format.DateTimeFormatterBuilder; |
29 | 32 | import java.time.format.DateTimeParseException; |
30 | 33 | import java.util.Calendar; |
31 | 34 | import java.util.Date; |
32 | 35 | import java.util.GregorianCalendar; |
33 | 36 | import java.util.Locale; |
34 | 37 | import java.util.SimpleTimeZone; |
35 | 38 | import java.util.TimeZone; |
36 | | -import java.util.regex.Matcher; |
37 | 39 | import java.util.regex.Pattern; |
38 | 40 |
|
39 | 41 | /** |
|
52 | 54 | public final class DateConverter |
53 | 55 | { |
54 | 56 |
|
| 57 | + public static final DateTimeFormatter DATE_TIME_FORMATTER = new DateTimeFormatterBuilder().parseCaseInsensitive() |
| 58 | + .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME).parseLenient().appendOffset("+HH:MM", "Z").parseStrict() |
| 59 | + .toFormatter(); |
| 60 | + |
55 | 61 | // The Date format is supposed to be the PDF_DATE_FORMAT, but not all PDF |
56 | 62 | // documents |
57 | 63 | // will use that date, so I have added a couple other potential formats |
@@ -100,19 +106,10 @@ public static Calendar toCalendar(String date) throws IOException |
100 | 106 | try |
101 | 107 | { |
102 | 108 | SimpleTimeZone zone = null; |
103 | | - |
| 109 | + |
104 | 110 | if (Pattern.matches("^\\d{4}-\\d{2}-\\d{2}T.*", date)) |
105 | 111 | { |
106 | | - // Assuming ISO860 date string |
107 | | - try |
108 | | - { |
109 | | - return fromISO8601(date, "yyyy-MM-dd'T'HH:mm:ss[.SSS][XXX][zzz]"); |
110 | | - } |
111 | | - catch (DateTimeParseException e) |
112 | | - { |
113 | | - // PDFBOX-6062: support nanoseconds |
114 | | - return fromISO8601(date, "yyyy-MM-dd'T'HH:mm:ss[.SSSSSS][XXX][zzz]"); |
115 | | - } |
| 112 | + return fromISO8601(date); |
116 | 113 | } |
117 | 114 | if (date.startsWith("D:")) |
118 | 115 | { |
@@ -339,77 +336,18 @@ public static String toISO8601(Calendar cal, boolean printMillis) |
339 | 336 | retval.append(minutes); |
340 | 337 | return retval.toString(); |
341 | 338 | } |
342 | | - |
343 | | - /** |
344 | | - * Get a Calendar from an ISO8601 date string. |
345 | | - * |
346 | | - * @param dateString |
347 | | - * @return the Calendar instance. |
348 | | - */ |
349 | | - private static Calendar fromISO8601(String dateString, String pattern) |
350 | | - { |
351 | | - DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern); |
352 | | - |
353 | | - // Pattern to test for a time zone string |
354 | | - Pattern timeZonePattern = Pattern.compile( |
355 | | - "[\\d-]*T?[\\d-\\.]([A-Z]{1,4})$|(.*\\d*)([A-Z][a-z]+\\/[A-Z][a-z]+)$" |
356 | | - ); |
357 | | - Matcher timeZoneMatcher = timeZonePattern.matcher(dateString); |
358 | | - |
359 | | - String timeZoneString = null; |
360 | | - |
361 | | - while (timeZoneMatcher.find()) |
362 | | - { |
363 | | - for (int i = 1; i <= timeZoneMatcher.groupCount(); i++) |
364 | | - { |
365 | | - String group = timeZoneMatcher.group(i); |
366 | | - if (group != null) |
367 | | - { |
368 | | - timeZoneString = group; |
369 | | - } |
370 | | - } |
371 | | - } |
372 | 339 |
|
373 | | - if (timeZoneString != null) |
| 340 | + private static Calendar fromISO8601(String dateString) |
| 341 | + { |
| 342 | + try |
374 | 343 | { |
375 | | - // can't use parseDateTime immediately, first do handling for time that has no seconds |
376 | | - int teeIndex = dateString.indexOf('T'); |
377 | | - int tzIndex = dateString.indexOf(timeZoneString); |
378 | | - String toParse = dateString.substring(0, tzIndex); |
379 | | - if (tzIndex - teeIndex == 6) |
380 | | - { |
381 | | - toParse = dateString.substring(0, tzIndex) + ":00"; |
382 | | - } |
383 | | - |
384 | | - ZonedDateTime zonedDateTime = ZonedDateTime.parse(toParse + timeZoneString, dateTimeFormatter); |
385 | | - |
| 344 | + ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateString, DATE_TIME_FORMATTER); |
386 | 345 | return GregorianCalendar.from(zonedDateTime); |
387 | 346 | } |
388 | | - else |
| 347 | + catch (DateTimeParseException e) |
389 | 348 | { |
390 | | - // can't use parseDateTime immediately, first do handling for time that has no seconds |
391 | | - int teeIndex = dateString.indexOf('T'); |
392 | | - if (teeIndex == -1) |
393 | | - { |
394 | | - ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateString, dateTimeFormatter); |
395 | | - return GregorianCalendar.from(zonedDateTime); |
396 | | - } |
397 | | - int plusIndex = dateString.indexOf('+', teeIndex + 1); |
398 | | - int minusIndex = dateString.indexOf('-', teeIndex + 1); |
399 | | - if (plusIndex == -1 && minusIndex == -1) |
400 | | - { |
401 | | - ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateString, dateTimeFormatter); |
402 | | - return GregorianCalendar.from(zonedDateTime); |
403 | | - } |
404 | | - plusIndex = Math.max(plusIndex, minusIndex); |
405 | | - if (plusIndex - teeIndex == 6) |
406 | | - { |
407 | | - String toParse = dateString.substring(0, plusIndex) + ":00" + dateString.substring(plusIndex); |
408 | | - ZonedDateTime zonedDateTime = ZonedDateTime.parse(toParse, dateTimeFormatter); |
409 | | - return GregorianCalendar.from(zonedDateTime); |
410 | | - } |
411 | | - ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateString, dateTimeFormatter); |
412 | | - return GregorianCalendar.from(zonedDateTime); |
| 349 | + LocalDateTime localDateTime = LocalDateTime.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE_TIME); |
| 350 | + return GregorianCalendar.from(localDateTime.atZone(ZoneId.of("UTC"))); |
413 | 351 | } |
414 | 352 | } |
415 | 353 | } |
0 commit comments