Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 LocalDateTime is parsing invalid date

I wanted to validate date in client side so I wrote the following code. But instead of getting an exception I am getting a proper date object for 31st of February date string, which is clearly an invalid date.

public class Test {

    public static void main(String[] args) {
        String dateFormat = "HH:mm:ss MM/dd/yyyy";
        String dateString = "11:30:59 02/31/2015";
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat, Locale.US);
        try {
            LocalDateTime date = LocalDateTime.parse(dateString, dateTimeFormatter);
            System.out.println(date);
        } catch (Exception e) {
            // Throw invalid date message
        }
    }
}

Output : 2015-02-28T11:30:59

Does anyone know why LocalDateTime is parsing this date instead of throwing an exception.

like image 219
Zeeshan Avatar asked Sep 28 '15 12:09

Zeeshan


4 Answers

You just need a strict ResolverStyle.

Parsing a text string occurs in two phases. Phase 1 is a basic text parse according to the fields added to the builder. Phase 2 resolves the parsed field-value pairs into date and/or time objects. This style is used to control how phase 2, resolving, happens.

Sample code - where withResolverStyle(ResolverStyle.STRICT) is the important change, along with the use of uuuu rather than yyyy (where uuuu is "year" and "yyyy" is "year of era", and therefore ambiguous):

import java.time.*;
import java.time.format.*;
import java.util.*;

public class Test {

    public static void main(String[] args) {
        String dateFormat = "HH:mm:ss MM/dd/uuuu";
        String dateString = "11:30:59 02/31/2015";
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter
            .ofPattern(dateFormat, Locale.US)
            .withResolverStyle(ResolverStyle.STRICT);
        try {
            LocalDateTime date = LocalDateTime.parse(dateString, dateTimeFormatter);
            System.out.println(date);
        } catch (DateTimeParseException e) {
            // Throw invalid date message
            System.out.println("Exception was thrown");
        }
    }
}
like image 113
Jon Skeet Avatar answered Sep 30 '22 12:09

Jon Skeet


The Java 8 DateTimeFormatter uses yyyy to mean YEAR_OF_ERA, and uuuu to mean YEAR. You need to modify your pattern string as follows:

String dateFormat = "HH:mm:ss MM/dd/uuuu";

The DateTimeFormatter defaults to using the SMART resolver style, but you want it to use the STRICT resolver style. Modify your dateTimeFormatter initialization code as follows:

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat, Locale.US)
                                                       .withResolverStyle(ResolverStyle.STRICT);
like image 30
Palamino Avatar answered Sep 30 '22 14:09

Palamino


It is not rounding down. February has never had 31 days, and it is impossible to use a validating date / time object to represent a day that doesn't exist.

As a result, it takes the invalid input and gives you the best approximation to the correct date (the last date of February that year).

SimpleDateFormat inherits from DateFormat which has a setLenient(boolean value) method on it. I would expect that if you called setLenient(true) prior to parsing, it would probably complain more, as detailed in the javadocs.

like image 35
Edwin Buck Avatar answered Sep 30 '22 12:09

Edwin Buck


try {
    SimpleDateFormat df = new java.text.SimpleDateFormat("HH:mm:ss MM/dd/yyyy");
    df.setLenient(false);
    System.out.println(df.parse("11:30:59 02/29/2015"));
} catch (java.text.ParseException e) {
  System.out.println(e);
}

I found one solution to recognize date as a valid date with DateFormat.setLenient(boolean). If you try to parse any invalid date it will throws parse exception.

Edit:

Java 8, but this will raise exception if a month is not between 1 and 12, if a day is more than 32. Exactly not working. But for month its working.

try {
TemporalAccessor ta = DateTimeFormatter.ofPattern("HH:mm:ss MM/dd/yyyy").parse("11:30:59 02/32/2015");
} catch (Exception e) {
System.out.println(e);
}

Output:

java.time.format.DateTimeParseException: Text '11:30:59 02/32/2015' could not be
 parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 32
like image 40
SatyaTNV Avatar answered Sep 30 '22 13:09

SatyaTNV