2

I'm trying to use Java 8 to re-format today's date but I'm getting the following error:

java.time.format.DateTimeParseException: Text '09-OCT-2017' could not be parsed: Unable to obtain LocalDate from TemporalAccessor: {WeekBasedYear[WeekFields[SUNDAY,1]]=2017, MonthOfYear=10, DayOfYear=9},ISO of type java.time.format.Parsed  

Code:

public static String formatDate(String inputDate, String inputDateFormat, String returnDateFormat){
    try {
        DateTimeFormatter inputFormatter = new DateTimeFormatterBuilder().parseCaseInsensitive().appendPattern(inputDateFormat).toFormatter(Locale.ENGLISH);            
        LocalDate localDate = LocalDate.parse(inputDate, inputFormatter);

        DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern(returnDateFormat);
        String formattedString = localDate.format(outputFormatter);
        return formattedString;
    } catch (DateTimeParseException dtpe) {
        log.error("A DateTimeParseException exception occured parsing the inputDate : " + inputDate + " and converting it to a " + returnDateFormat + " format. Exception is : " + dtpe);           
    }
    return null;
}

I previously tried using SimpleDateFormat, but the problem is my inputDateFormat format is always in uppercase DD-MMM-YYYY, which was giving me incorrect results, so I tried using parseCaseInsensitive() to ignore the case sensitivity.

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
Orby
  • 428
  • 1
  • 9
  • 24
  • 2
    Well, `MonthOfYear=10` and `DayOfYear=9` don't fit, since the 9th day of a year is _not_ in October for sure. This is due to the input format using `DD` which is plainly wrong. – Thomas Oct 09 '17 at 11:13
  • Also note that `parseCaseInsensitive()` is meant to ignore the case of the date being parsed, not the pattern being passed, i.e. to treat `09-OCT-2017` and`09-oct-2017` etc. equally. – Thomas Oct 09 '17 at 11:19
  • The format of inputDateFormat is `DD-MMM-YYYY` so is my pattern wrong? – Orby Oct 09 '17 at 11:21
  • @thomas, thanks for the clarification around `parseCaseInsensitive()`. My pattern is always in uppercase and this is causing my issue. – Orby Oct 09 '17 at 11:24

2 Answers2

2

In the comments you told that the input format is DD-MMM-YYYY. According to javadoc, uppercase DD is the day of year field, and YYYY is the week based year field (which might be different from the year field).

You need to change them to lowercase dd (day of month) and yyyy (year of era). The parseCaseInsensitive() only takes care of the text fields - in this case, the month name (numbers are not affected by the case sensitivity - just because the month is in uppercase, it doesn't mean that the numbers patterns should also be).

The rest of the code is correct. Example (changing the format to yyyyMMdd):

String inputDate = "09-OCT-2017";
DateTimeFormatter inputFormatter = new DateTimeFormatterBuilder()
    .parseCaseInsensitive()
    // use "dd" for day of month and "yyyy" for year
    .appendPattern("dd-MMM-yyyy")
    .toFormatter(Locale.ENGLISH);
LocalDate localDate = LocalDate.parse(inputDate, inputFormatter);

// use "dd" for day of month and "yyyy" for year
DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
String formattedString = localDate.format(outputFormatter);
System.out.println(formattedString); // 20171009

The output of the code above is:

20171009


Regarding your other comment about not having control over the input pattern, one alternative is to manually replace the letters to their lowercase version:

String pattern = "DD-MMM-YYYY";
DateTimeFormatter inputFormatter = new DateTimeFormatterBuilder()
    .parseCaseInsensitive()
    // replace DD and YYYY with the lowercase versions
    .appendPattern(pattern.replace("DD", "dd").replaceAll("YYYY", "yyyy"))
    .toFormatter(Locale.ENGLISH);
// do the same for output format if needed

I don't think it needs a complex-replace-everything-in-one-step regex. Just calling the replace method multiple times can do the trick (unless you have really complex patterns that would require lots of different and complex calls to replace, but with only the cases you provided, that'll be enough).

  • Thanks, the reason the code doesn't work is because the pattern i.e. DD-MMM-YYYY is always stored in the variable `inputDateFormat` as UPPERCASE. I have no control over this so what I need is a regex pattern to convert the above format to dd-MMM-yyyy – Orby Oct 09 '17 at 12:33
  • @Orby I've updated the answer (no need for regex actually, a simple replace can solve it). But please, next time don't forget to include all those details in the question (you can also edit it at anytime to include more information). Without all the details and requirements, people can't answer it properly. –  Oct 09 '17 at 12:38
  • 1
    Thanks, this is what i was trying to achieve with a regex! – Orby Oct 09 '17 at 12:46
  • @Orby You're welcome, glad to help! Although regex is a great and useful thing (I like it and used a lot), [it's not always the best solution](http://regex.info/blog/2006-09-15/247). Use it [when really needed](https://blog.codinghorror.com/regular-expressions-now-you-have-two-problems/) –  Oct 09 '17 at 12:51
  • 1
    Agreed, this a simple use case! Thanks again for your help :) – Orby Oct 09 '17 at 12:53
  • A formatter for pattern `yyyyMMdd` is built into java.time as a constant: [DateTimeFormatter.BASIC_ISO_DATE](https://docs.oracle.com/javase/9/docs/api/java/time/format/DateTimeFormatter.html#BASIC_ISO_DATE) – Basil Bourque Oct 09 '17 at 15:16
  • @BasilBourque I know. But in the question, the output format is a parameter, so I just gave an example that matches the OP's code. –  Oct 09 '17 at 15:40
  • 1
    @Hugo Of course, well done. My comment is just an FYI for others. – Basil Bourque Oct 09 '17 at 15:50
-1

I hope I got you right. Formatting a String to LocalDate is acutally pretty simple. Your date format is that here right 09-Oct-2017? Now you just need use the split command to divide that into a day, month and year:

String[] tempStr = inputDate.split("-");
int year = Integer.parseInt(tempStr[2]);
int month = Integer.parseInt(tempStr[1]);
int day = Integer.parseInt(tempStr[0]);

After that it´s pretty easy to get that to LocalDate:

LocalDate ld = LocalDate.of(year, month, day);

I hope that helps.

  • Thanks, my input dates could be in several different formats so the above won't always work. – Orby Oct 09 '17 at 11:28
  • 1
    To be honest it´s never a good idea to let the user decide how he date should look like. Do you use some kind of GUI Calender? – Alexander Heim Oct 09 '17 at 11:48