Evgeniy
Evgeniy

Reputation: 146

LocalDateTime parse short year format dd-MMM-YY HH:mm using DateTimeFormatterBuilder

I have been trying to create DateUtil for parsing a string to LocalDateTime. There is no problems if a year has pattern "yyyy" but I get Exception if year represents like "yy".

Exception in thread "main" java.time.format.DateTimeParseException: Text '06-mar-17 11:52' could not be parsed at index 3
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
at java.time.LocalDateTime.parse(LocalDateTime.java:492)
at rinex.service.impl.utils.date.Test.parseToLocalDateTime(Test.java:43)
at rinex.service.impl.utils.date.Test.main(Test.java:53)

Test class

    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    import java.time.format.DateTimeFormatterBuilder;
    import java.time.format.DateTimeParseException;
    import java.time.temporal.ChronoField;
    import java.util.LinkedHashMap;
    import java.util.Map;

    public class Test {

    private static final Map<String, String> DATE_FORMAT_REGEXPS = new LinkedHashMap<String, String>() {{
            put("^\\d{4}\\d{1,2}\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyyMMdd HH:mm:ss");
            put("^\\d{1,2}-[a-z]{3}-\\d{2}\\s\\d{1,2}:\\d{2}$", "dd-MMM-yy HH:mm");
        }};

    private static final Map<String, DateTimeFormatter> DATE_SHORT_FORMAT_REGEXPS = new LinkedHashMap<String, DateTimeFormatter>() {{
        put("dd-MMM-yy HH:mm", new DateTimeFormatterBuilder().
                appendPattern("dd-MMM-").
                appendValueReduced(ChronoField.YEAR, 2, 2, 2000).
                appendPattern(" HH:mm").toFormatter());
    }};

    public static String determineDateFormat(String dateString) {
        for (String regexp : DATE_FORMAT_REGEXPS.keySet()) {
            if (dateString.toLowerCase().matches(regexp)) {
                return DATE_FORMAT_REGEXPS.get(regexp);
            }
        }
        return null; // Unknown format.
    }

    public static LocalDateTime parseToLocalDateTime(String date) {
        date = date.trim().toLowerCase().replaceAll("utc","");
        String format = determineDateFormat(date);
        LocalDateTime local;

        try {
            local = LocalDateTime.parse(date, DateTimeFormatter.ofPattern(format));
        } catch (DateTimeParseException e) {
            DateTimeFormatter formatter = DATE_SHORT_FORMAT_REGEXPS.get(format);
            local = LocalDateTime.parse(date, formatter);
        }
        return local;
    }

    public static void main(String[] args) {
        String date = "20170506 11:52:00UTC";
        LocalDateTime dateTime = Test.parseToLocalDateTime(date);

        date = "06-MAR-17 11:52";
        dateTime = Test.parseToLocalDateTime(date);
    }
}

Debug for "20170506 11:52:00UTC" date - "yyyyMMdd HH:mm:ss"

Debug for "06-MAR-17 11:52" date - "dd-MMM-yy HH:mm"

So, what is wrong in DateTimeFormatterBuilder to parse "06-MAR-17 11:52" date?

Upvotes: 2

Views: 3368

Answers (1)

Marvin
Marvin

Reputation: 14415

It's not the year, but the string representation of the month, where the parsing is case sensitive by default.

You can either use the proper (title) case:

// works
LocalDateTime.parse("06-Mar-17 11:52",
        DateTimeFormatter.ofPattern("dd-MMM-yy HH:mm", Locale.ENGLISH));

Or you can build a DateTimeFormatter that parses case insensitive:

// works
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
        .parseCaseInsensitive().appendPattern("dd-MMM-yy HH:mm")
        .toFormatter(Locale.ENGLISH);
formatter.parse("06-mar-17 11:52");

(The explicit locale is not required if your default locale is already English.)

Upvotes: 4

Related Questions