BruceyBandit
BruceyBandit

Reputation: 4324

Unable to parse a Month+day using DateTimeFormatter

I tried looking at this link for inspiration: Parsing a date’s ordinal indicator ( st, nd, rd, th ) in a date-time string

However I am getting an error when I try to parse a string "Mon 21st May" to "Monday 21st May" - full day name, date number and full month.

Exception in thread "main" java.time.format.DateTimeParseException: Text 'Mon 21st May' could not be parsed at index 0 at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949) at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851) at java.time.LocalDate.parse(LocalDate.java:400) at HelloWorld.main(HelloWorld.java:45)

Here is the code:

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.text.SimpleDateFormat;  
import java.util.Date;  
import java.util.Locale;

// one class needs to have a main() method
public class HelloWorld
{
  // arguments are passed using the text field below this editor
  public static void main(String[] args) throws Exception
  {

    String str = "Mon 21st May";
    DateTimeFormatter parseFormatter = DateTimeFormatter.ofPattern("EEEE d['st']['nd']['rd']['th'] MMMM", Locale.ENGLISH);
    LocalDate datetext = LocalDate.parse(str, parseFormatter);

  }
}

UPDATE:

Here is the latest code I have tried following suggestions and I changed the string to see if the issue is with that and below is there error I now receive. Looks like it knows the dates, just not able to parse it:

  public static void main(String[] args) throws Exception
  {

    String str = "Tue 21st May";
    DateTimeFormatter parseFormatter = DateTimeFormatter.ofPattern("EEE d['st']['nd']['rd']['th'] MMMM", Locale.ENGLISH);
    LocalDate datetext = LocalDate.parse(str, parseFormatter);

  }
}

Error:

Exception in thread "main" java.time.format.DateTimeParseException: Text 'Tue 21st May' could not be parsed: Unable to obtain LocalDate from TemporalAccessor: {DayOfWeek=2, DayOfMonth=21, MonthOfYear=5},ISO of type java.time.format.Parsed at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1920) at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1855) at java.time.LocalDate.parse(LocalDate.java:400) at HelloWorld.main(HelloWorld.java:26) Caused by: java.time.DateTimeException: Unable to obtain LocalDate from TemporalAccessor: {DayOfWeek=2, DayOfMonth=21, MonthOfYear=5},ISO of type java.time.format.Parsed at java.time.LocalDate.from(LocalDate.java:368) at java.time.format.Parsed.query(Parsed.java:226) at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851) ... 2 more

Upvotes: 0

Views: 1070

Answers (3)

djangofan
djangofan

Reputation: 29669

Here is my code to search for correct year:

Without knowing the day-name of the week, you have a 6/7 chance of parsing the date into the incorrect year. BUT, if you know the day-name, then you can search a 7 year range to find the correct year.

Assuming you have either a date like one of the following:

Wednesday, May 27
WED 5/27

And formatters like:

DateTimeFormatter visibleButtonFormat = new DateTimeFormatterBuilder()
            .parseCaseInsensitive()
            .appendPattern("EEE M/dd")
            .toFormatter(Locale.US);
        DateTimeFormatter accessibleFormat = DateTimeFormatter.ofPattern("EEEE, MMMM dd");

Then, you can parse a MonthDay like so:

MonthDay monthDay = MonthDay.parse(dateText, oneOfTheAboveFormatters);

Then, you can search for the correct year. In this case it is 2020:

private static int findYearMatchingMonthDay(String dateText, MonthDay monthDay) {
    for(int checkYear = 2020; checkYear >= 2016; checkYear--) {
        LocalDate localDate = LocalDate.of(checkYear, monthDay.getMonth(), monthDay.getDayOfMonth());
        String shortDay = localDate.getDayOfWeek().getDisplayName(TextStyle.SHORT, Locale.US).toUpperCase();
        if (shortDay.equals(dateText.substring(0,3))) {
            ConsoleUtils.logDebug("Assuming schedule buttons within year: " + checkYear);
            return checkYear;
        }
    }
    return Year.now().getValue();
}

Upvotes: 0

Anonymous
Anonymous

Reputation: 86130

MonthDay

If you want to parse month and day of month without a year, use MonthDay:

    String str = "Mon 21st May";
    DateTimeFormatter parseFormatter = DateTimeFormatter.ofPattern("EEE d['st']['nd']['rd']['th'] MMMM", Locale.ENGLISH);
    MonthDay md = MonthDay.parse(str, parseFormatter);
    System.out.println(md);

Output is:

--05-21

The leading dash indicates an absent year. EEEE in the format pattern string is for full name of the day of week, like Monday or Tuesday. For the abbreviation you need either E, EE or EEE.

LocalDate

If you want a LocalDate, you need to supply a year somehow. One option is:

    LocalDate datetext = md.atYear(Year.now(ZoneId.of("Europe/London")).getValue());
    System.out.println(datetext);

2019-05-21

This doesn’t validate the day of month, though. To do that:

    String str = "Tue 21st May";
    DateTimeFormatter parseFormatter = new DateTimeFormatterBuilder()
            .appendPattern("EEE d['st']['nd']['rd']['th'] MMMM")
            .parseDefaulting(ChronoField.YEAR, Year.now(ZoneId.of("Europe/London")).getValue())
            .toFormatter(Locale.ENGLISH);

    LocalDate datetext = LocalDate.parse(str, parseFormatter);

2019-05-21

In the comment you asked:

What about if I want to output it as Tuesday 21 May?

It’s sort of a new question and has been covered many times, but OK.

    DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("EEEE d MMMM", Locale.ENGLISH);
    System.out.println(datetext.format(outputFormatter));

Tuesday 21 May

Detecting year from day of week

I may not know the year, as in I am trying to view dates in an application but some dates may be this year, some may fall over to next year but these dates don't have a year supplied

The following complete example assumes the date falls within the next 3 years from today and detects the year where the day of week is correct. On success it prints in your desired format.

    String str = "Wed 20th May";
    ZoneId zone = ZoneId.of("Europe/London");
    LocalDate today = LocalDate.now(zone);
    int currentYear = today.getYear();
    LocalDate datetext = null;
    final int maxYearsFromToday = 3;
    for (int year = currentYear; year <= currentYear + maxYearsFromToday; year++) {
        DateTimeFormatter parseFormatter = new DateTimeFormatterBuilder()
                .appendPattern("EEE d['st']['nd']['rd']['th'] MMMM")
                .parseDefaulting(ChronoField.YEAR, year)
                .toFormatter(Locale.ENGLISH);

        try {
            datetext = LocalDate.parse(str, parseFormatter);
            System.out.println("Day of week matched for year " + year);
            break;
        } catch (DateTimeParseException dtpe) {
            // Ignore, try next year
        }
    }

    DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("EEEE d MMMM", Locale.ENGLISH);
    if (datetext == null) {
        System.out.println("Could not parse date;"
                + " possibly the day of week didn’t match for any year in the range "
                + currentYear + " through " + (currentYear + maxYearsFromToday));
    } else if (datetext.isBefore(today) || datetext.isAfter(today.plusYears(maxYearsFromToday))) {
        System.out.println("Date is out of range");
    } else {
        System.out.println("Successfully parsed: " + datetext.format(outputFormatter));
    }
Day of week matched for year 2020
Successfully parsed: Wednesday 20 May

Upvotes: 4

Johan Claes
Johan Claes

Reputation: 139

It's Tuesday 21st May 2019. The code below works.

    String str = "Tue 21st May 2019";
    DateTimeFormatter parseFormatter = DateTimeFormatter.ofPattern("EEE d['st']['nd']['rd']['th'] MMMM yyyy", Locale.ENGLISH);
    LocalDate datetext = LocalDate.parse(str, parseFormatter);

Upvotes: 1

Related Questions