LocalDateTime unable to parse “June”

I had help setting up a function to take two strings and merge them into a date object.Java - Take two strings and combine into a single date object

This has been working fine until it tries to parse 1st June, then i get the below error

      Exception in thread "AWT-EventQueue-0" java.time.format.DateTimeParseException: Text '1st June' could not be parsed, unparsed text found at index 7
    at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1952)
    at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
    at java.time.LocalDate.parse(LocalDate.java:400)
    at Timetable.ClassManager.parseDate(ClassManager.java:201)
    at Timetable.GoogleAPI.loadClasses(GoogleAPI.java:133)
    at Timetable.ClassManager.loadClasses(ClassManager.java:58)

The code for the function is

      public LocalDateTime parseDate(String strDate, String strTime) {
    strTime = strTime + ":00";
    System.out.println("Date: " + strDate);
    System.out.println("Time: " + strTime);
    
    
    LocalDate date = LocalDate.now();
    DateTimeFormatter dtfForDate = new DateTimeFormatterBuilder()
                                    .parseCaseInsensitive()
                                    .parseDefaulting(ChronoField.YEAR, date.getYear())
                                    .appendPattern("d['th']['st']['rd']['nd'] MMM")
                                    .toFormatter(Locale.ENGLISH);

    DateTimeFormatter dtfForTime = DateTimeFormatter.ofPattern("H:m:s", Locale.ENGLISH);
    
    LocalDateTime ldt = LocalDate.parse(strDate, dtfForDate)
                                    .atTime(LocalTime.parse(strTime, dtfForTime));
    System.out.println("Local Date Time: " + ldt);
    
    return ldt;
}

The two prints give me

      Date: 1st June

Time: 9:15:00

I would need to be able to handle both full month names and month abbreviations, i.e., March was set as Mar, April as Apr.

1 ответ

Решение

Use the following pattern which will cater to both, the three-letter abbreviated month names as well as the full month names:

      .appendPattern("d['th']['st']['rd']['nd'] [MMMM][MMM]")

Demo:

      import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        System.out.println(parseDate("1st June", "9:15:00"));
        System.out.println(parseDate("1st Jun", "9:15:00"));
    }

    public static LocalDateTime parseDate(String strDate, String strTime) {
        LocalDate date = LocalDate.now();
        DateTimeFormatter dtfForDate = new DateTimeFormatterBuilder()
                                        .parseCaseInsensitive()
                                        .parseDefaulting(ChronoField.YEAR, date.getYear())
                                        .appendPattern("d['th']['st']['rd']['nd'] [MMMM][MMM]")
                                        .toFormatter(Locale.ENGLISH);

        DateTimeFormatter dtfForTime = DateTimeFormatter.ofPattern("H:m:s", Locale.ENGLISH);

        LocalDateTime ldt = LocalDate.parse(strDate, dtfForDate).atTime(LocalTime.parse(strTime, dtfForTime));

        return ldt;
    }
}

Output:


Update

This update addresses the following concern raised by Meno Hochschild:

Personally, I don't like the misuse of optional sections here.Negative example: "1th Jun" or "2st Jun" would also be successfullyparsed but should not.

      import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        System.out.println(parseDate("1st June", "9:15:00"));
        System.out.println(parseDate("1st Jun", "9:15:00"));
    }

    public static LocalDateTime parseDate(String strDate, String strTime) {
        LocalDate date = LocalDate.now();
        DateTimeFormatter dtfForDate = new DateTimeFormatterBuilder()
                                        .parseCaseInsensitive()
                                        .parseDefaulting(ChronoField.YEAR, date.getYear())
                                        .appendText(ChronoField.DAY_OF_MONTH, ordinalMap())
                                        .appendPattern(" [MMMM][MMM]")
                                        .toFormatter(Locale.ENGLISH);

        DateTimeFormatter dtfForTime = DateTimeFormatter.ofPattern("H:m:s", Locale.ENGLISH);

        LocalDateTime ldt = LocalDate.parse(strDate, dtfForDate).atTime(LocalTime.parse(strTime, dtfForTime));

        return ldt;
    }

    static Map<Long, String> ordinalMap() {
        String[] suffix = { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" };
        Map<Long, String> map = new HashMap<>();
        for (int i = 1; i <= 31; i++) {
            int m = i % 100;
            map.put((long)i, String.valueOf(i) + suffix[(m > 3 && m < 21) ? 0 : (m % 10)]);
        }
        return map;
    }
}

Output:

      2021-06-01T09:15
2021-06-01T09:15

Courtesy: The logic to build the Map is based on this excellent answer.

Другие вопросы по тегам