1

I am trying to validate incoming date string and then convert it to a LocalDate. Here is my code:

public class DateTester {

    public static void main(String[] args) {
        System.out.println("converted date is" +stringToLD("2023-01-23")); 
        System.out.println("converted date is" +stringToLD("2023-13-21")); 
        System.out.println("converted date is" +stringToLD("2023-24-31")); 
        System.out.println("converted date is" +stringToLD("2023-36-34"));

//        converted date is 2023-01-23
//        converted date is2024-01-21
//        converted date is2024-12-31
//        converted date is2026-01-03 
    }

    public static LocalDate stringToLD(String inputDate) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date date;
        try {
            date = sdf.parse(inputDate);
        } catch (ParseException e) {
            throw new IllegalArgumentException("Unable to parse date, " + inputDate);
        }
        return LocalDate.parse(sdf.format(date));
    }
}

but however when I send invalid date like 2023-13-21 I get back converted date as 2024-01-21 which is invalid and unwanted result. So I wanted to understand why this is happening and also looking for an alternate solution in java 8

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
happytohelp
  • 305
  • 3
  • 14
  • *looking for an alternate solution in java 8* Yes, this is 100 % what you want. What you have observed is how `SimpleDateFormat` misbehaves. Also you have got nothing to use it for here. Try the no-arg `LocalDate.parse()` without giving any formatter, or try `DateTimeFormatter.ISO_LOCAL_DATE`. `LocalDate.parse("2023-02-29")` and `LocalDate.parse("2023-13-21")` both throw exceptions. – Ole V.V. Jan 26 '23 at 06:45

1 Answers1

3

Try it like this. Don't use Date or SimpleDateFormat. As stated by Ole V.V the default, strict, format is DateTimeFormatter.ISO_LOCAL_DATE is used by LocalDate.parse(). That also matches your requirements.

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;

public static LocalDate stringToLD(String inputDate) {
    
    try {
        return LocalDate.parse(inputDate);
       
    } catch (DateTimeParseException e) {
        throw new IllegalArgumentException("Unable to parse date, " + inputDate);
    }
}
WJS
  • 36,363
  • 4
  • 24
  • 39
  • same results actually may be I have to use .withResolverStyle(STRICT) – happytohelp Jan 26 '23 at 03:08
  • 1
    What do you mean "same results?" Only the first call succeeds. The others throw exceptions. – WJS Jan 26 '23 at 03:09
  • you are correct, I had to recompile and restart my server. it worked. Thank you!! – happytohelp Jan 26 '23 at 03:16
  • btw any idea why sdf wont work? – happytohelp Jan 26 '23 at 03:16
  • 1
    *btw any idea why sdf wont work?* I'm not certain. The JavaDoc doesn't say but I believe it is doing a lenient parse and correcting the date to match. – WJS Jan 26 '23 at 03:21
  • 2
    OK I found it. SimpleDateFormat extends DateFormat which has a `setLenient` method. It defaults to `lenient` as shown by print `sdf.isLenient()` Had you done a `setLenient(false)` it would probably have worked for you. But I would still use `LocalDate` and stay away from the legacy date/time methods as they are buggy. – WJS Jan 26 '23 at 03:27
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/251399/discussion-between-happytohelp-and-wjs). – happytohelp Jan 26 '23 at 03:45
  • 1
    2003-02-31 will not throw error with above code – happytohelp Jan 26 '23 at 03:54
  • Do not give any explicit formatter here. The one-arg `LocalDate.parse(CharSequence)` resolves strictly so rejects for example `2003-02-31`. – Ole V.V. Jan 26 '23 at 06:48
  • There are first of all many ways in which `SimpleDateFormat` won’t work and therefore many corresponding reasons. Leniency is just one of the problems. – Ole V.V. Jan 26 '23 at 06:55
  • @OleV.V. Why would some be rejected and others not. Seems sort of partially strict. – WJS Jan 26 '23 at 11:18
  • The formatter you get from `ofPattern()` is “smart” which is supposed to be something between lenient and strict. From [the documentation](https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/time/format/ResolverStyle.html#SMART): *For example, resolving year-month and day-of-month in the ISO calendar system using smart mode will ensure that the day-of-month is from 1 to 31, converting any value beyond the last valid day-of-month to be the last valid day-of-month.* – Ole V.V. Jan 26 '23 at 11:29
  • @OleV.V., so if I use one arg, how can I format the date to my requirement (2003-02-31)? or do you mean its by default formats in yyyy-MM-dd? – happytohelp Jan 26 '23 at 14:09
  • 1
    @happytohelp Yes that is the default. `DateTimeFormatter.ISO_LOCAL_DATE` which matches your dashed format. – WJS Jan 26 '23 at 14:15
  • @happytohelp I modified my answer to include that information. – WJS Jan 26 '23 at 14:23
  • @happytohelp Are you overthinking it? I thought you asked about converting a string to a `LocalDate`, that which is called *parsing*. Do you want to *format* instead? A `LocalDate` cannot have a format, only a string can. And `LocalDate.toString()` produces a string in `yyyy-MM-dd` format. Really `uuuu-MM-dd`, but for years in the 1–9999 range they are the same. The full explanation is [here](https://stackoverflow.com/questions/41177442/uuuu-versus-yyyy-in-datetimeformatter-formatting-pattern-codes-in-java). – Ole V.V. Jan 26 '23 at 14:24
  • 1
    @OleV.V. I'm pretty certain the OP is concerned about the source String being able to be parsed correctly. And since it is specified in ISO_LOCAL_DATE format, it will work, as you explained in the comments. The ultimate desired output format was never discussed. – WJS Jan 26 '23 at 14:25