2

I have a record which have an abnormal DateTime String '20170-09-17',I tried to parse it into DateTime but it didn't throw any Exceptions and this record can not put into MySQL as well...

        DateFormat fmt =new SimpleDateFormat("yyyy-MM-dd");
        System.out.println(fmt.parse("20170-09-17"));

What to do with this data?

javadev
  • 688
  • 2
  • 6
  • 17
Dodge_X
  • 37
  • 8
  • That won't ever be a valid date. You need to remove that 0. – WrightsCS Jan 25 '21 at 03:47
  • @WrightsCS Did you read his question properly? He himself told it's not a valid date but the parsing is okay which ideally shouldn't. You din't understood his question properly.. – Stunner Jan 25 '21 at 03:48
  • @Stunner which is why I said "You need to remove that 0". – WrightsCS Jan 25 '21 at 03:49
  • 1
    I don't think I can remove it if I can't validate it. – Dodge_X Jan 25 '21 at 03:52
  • @WrightsCS I guess he meant if he inputs invalid date , he is expecting a parse exception instead the program continues fine without any exception. – Stunner Jan 25 '21 at 03:52
  • 1
    @DodgeXiong Read this https://www.baeldung.com/java-date-regular-expressions. Use regular expression , pattern match to validate your date. – Stunner Jan 25 '21 at 03:56
  • @Stunner that is helpful,Thanks for you both. – Dodge_X Jan 25 '21 at 04:05
  • 1
    I would recommend not using `SimpleDateFormat` to start with and in instead use the `DateTimeFormatter` from the `java.time` APIs – MadProgrammer Jan 25 '21 at 04:28

3 Answers3

1

java.time

I always recommend to use java.time, the modern Java date and time API, for date work. In this case it makes your task pretty simple.

    String abnormalInput = "20170-09-17";
    LocalDate date = LocalDate.parse(abnormalInput);
    System.out.println(date);

This throws the exception that you had expected:

Exception in thread "main" java.time.format.DateTimeParseException: Text '20170-09-17' could not be parsed at index 0

We don’t even need to specify any formatter. Your expected input is in ISO 8601 format (link at the bottom), and LocalDate parses ISO 8601 as its default. And throws an exception if the text to parse does not adhere to the format. It can accept a year with more than four digits, but then only with a sign (minus or plus). Because the standard says so.

The range check that Andreas suggests in his answer could still be a good idea. I suggest that you set the limits not by what MySQL can handle but by what can be considered correct in your domain. For the sake of an example:

    LocalDate today = LocalDate.now(ZoneId.of("Asia/Shanghai"));
    if (date.getYear() < today.getYear() - 5 || date.isAfter(today)) {
        throw new IllegalStateException("Date is out of range: " + date);
    }

Links

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • Very well answered! Both the points (regarding the exception in formatting and regarding the validation of the year) provide with the recommended way to solve this problem. The first point, `We don’t even need to specify any formatter.` is spot-on. – Arvind Kumar Avinash Jan 25 '21 at 19:59
0

SimpleDateFormat doesn't know the supported range of year where the date will be stored, so you need to validation the date.

Since you mentioned MySQL, and the DATETIME data type supports years in range 1000-9999, you should do this:

static Date parseDate(String dateStr) throws ParseException {
    // Parse the string (strict)
    DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
    fmt.setLenient(false); // Reject invalid month and day values
    Date date = fmt.parse(dateStr);
    
    // Validate year
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    int year = cal.get(Calendar.YEAR);
    if (year < 1000 || year > 9999)
        throw new IllegalArgumentException("Invalid year: \"" + dateStr + "\"");
    
    return date;
}

The important parts are:

  • Turn on strict parsing mode, to ensure month and day values are in valid ranges.

  • Custom check to ensure year value is in valid range.


It might be better if you could use the newer Java 8+ Time API, in which case the code would be:

static LocalDate parseDate(String dateStr) {
    DateTimeFormatter fmt = DateTimeFormatter.ofPattern("uuuu-MM-dd")
            .withResolverStyle(ResolverStyle.STRICT);
    LocalDate date = LocalDate.parse(dateStr, fmt);
    if (date.getYear() < 1000 || date.getYear() > 9999)
        throw new DateTimeException("Invalid value for Year (valid values 1000 - 9999): " + date.getYear());
    return date;
}

Same as before: Strict parsing and custom year range check.

Andreas
  • 154,647
  • 11
  • 152
  • 247
0

SimpleDateFormat and Date are outdated classes. If you use java 8 or higher please switch to DateTimeFormatter class and classes like LocalDate LocalDateTime or any of their "brothers"

Michael Gantman
  • 7,315
  • 2
  • 19
  • 36