Java: Unable to obtain LocalDate from TemporalAccessor
Asked Answered
C

5

24

I am trying to change the format of a String date from EEEE MMMM d to MM/d/yyyy by, first, converting it into a LocalDate and then applying a formatter of a different pattern to the LocalDate before parsing it into String again.

Here's my code:

private String convertDate(String stringDate) 
{
    //from EEEE MMMM d -> MM/dd/yyyy

    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .parseCaseInsensitive()
            .append(DateTimeFormatter.ofPattern("EEEE MMMM d"))
            .toFormatter();

    LocalDate parsedDate = LocalDate.parse(stringDate, formatter);
    DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("MM/d/yyyy");

    String formattedStringDate = parsedDate.format(formatter2);

    return formattedStringDate;
}

However, I get this exception message that I don't really understand:

Exception in thread "main" java.time.format.DateTimeParseException: Text 'TUESDAY JULY 25' could not be parsed: Unable to obtain LocalDate from TemporalAccessor: {DayOfWeek=2, MonthOfYear=7, DayOfMonth=25},ISO of type java.time.format.Parsed
    at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1920)
Cataclinal answered 26/7, 2017 at 8:6 Comment(3)
Well, you can't have a date without a year.Potful
You haven't said what you expect the year to be. The current year? Always 2017? Work out the year based on the combination of day-of-month and day-of-week? Either KayV or Hugo is closest depending on your use case.Connatural
Near-duplicate of Java Autocomplete/Format Date from MM-DD inputParaffin
S
15

As the other answers already said, to create a LocalDate you need the year, which is not in the input String. It has only day, month and day of the week.

To get the full LocalDate, you need to parse the day and month and find a year in which this day/month combination matches the day of the week.

Of course you could ignore the day of the week and assume that the date is always in the current year; in this case, the other answers already provided the solution. But if you want to find the year that exactly matches the day of the week, you must loop until you find it.

I'm also creating a formatter with a java.util.Locale, to make it explicit that I want month and day of week names in English. If you don't specify a locale, it uses the system's default, and it's not guaranteed to always be English (and it can be changed without notice, even at runtime).

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    .parseCaseInsensitive()
    .append(DateTimeFormatter.ofPattern("EEEE MMMM d"))
    // use English Locale to correctly parse month and day of week
    .toFormatter(Locale.ENGLISH);
// parse input
TemporalAccessor parsed = formatter.parse("TUESDAY JULY 25");
// get month and day
MonthDay md = MonthDay.from(parsed);
// get day of week
DayOfWeek dow = DayOfWeek.from(parsed);
LocalDate date;
// start with some arbitrary year, stop at some arbitrary value
for(int year = 2017; year > 1970; year--) {
    // get day and month at the year
    date = md.atYear(year);
    // check if the day of week is the same
    if (date.getDayOfWeek() == dow) {
        // found: 'date' is the correct LocalDate
        break;
    }
}

In this example, I started at year 2017 and tried to find a date until back to 1970, but you can adapt to the values that fits your use cases.

You can also get the current year (instead of some fixed arbitrary value) by using Year.now().getValue().

Stockton answered 26/7, 2017 at 12:5 Comment(0)
E
13

The documentation for LocalDate says, that

LocalDate is an immutable date-time object that represents a date, often viewed as year-month-day. For example, the value "2nd October 2007" can be stored in a LocalDate.

In your case, the input String is missing an important component of LocalDate , i.e the year. What you have basically is month and day. So, you can use a class suited to that MonthDay. Using that your code can be modified to :

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                .parseCaseInsensitive()
                .append(DateTimeFormatter.ofPattern("EEEE MMMM d"))
                .toFormatter();

 MonthDay monthDay = MonthDay.parse(stringDate, formatter);
 LocalDate parsedDate = monthDay.atYear(2017); // or whatever year you want it at
 DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("MM/d/yyyy");

 String formattedStringDate = parsedDate.format(formatter2);
 System.out.println(formattedStringDate); //For "TUESDAY JULY 25" input, it gives the output 07/25/2017 
Eb answered 26/7, 2017 at 8:48 Comment(0)
L
4

Here is the minor change which you need to implement:

private static String convertDate(String stringDate) 
{
    //from EEEE MMMM d -> MM/dd/yyyy

    DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendPattern("EEEE MMMM dd")
                                        .parseDefaulting(ChronoField.YEAR, 2017)
                                        .toFormatter();

    LocalDate parsedDate = LocalDate.parse(stringDate, formatter);
    DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("MM/d/yyyy");

    String formattedStringDate = parsedDate.format(formatter2);

    return formattedStringDate;
}
  1. Add the default chronological year in the formatter using .parseDefaulting(ChronoField.YEAR, 2017)

  2. Call the method using the argument "Tuesday July 25" like this convertDate("Tuesday July 25");

Leitmotiv answered 26/7, 2017 at 8:55 Comment(1)
I believe that this validates that the day of week of the obtained date agrees with the day of week in the input string, which I suppose we want. So that’s good.Paraffin
A
0

This is interesting it helped me understand that for java.time.LocalTime requires HH:mm:ss skipping Hrs wont' work.

//Works
LocalTime time1 = LocalTime.parse("10:15:30", DateTimeFormatter.ofPattern("HH:mm:ss"));
        LocalTime time2 = LocalTime.parse("12:00:00", DateTimeFormatter.ofPattern("HH:mm:ss"));
//Fails
LocalTime time1 = LocalTime.parse("15:30", DateTimeFormatter.ofPattern("mm:ss"));
        LocalTime time2 = LocalTime.parse("00:00", DateTimeFormatter.ofPattern("mm:ss"));
Abase answered 28/3 at 5:51 Comment(0)
T
-1

Another option is to do the following (just like the other answers a bit hacky), assuming of course you want the date to fall in the current year:

LocalDate localDate = LocalDate.parse(stringDate + " " +LocalDate.now().getYear(), DateTimeFormatter.ofPattern("EEEE MMMM d");
Tattle answered 26/7, 2017 at 9:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.