36

I wanted to validate datetime in client side so I wrote the following code. But instead of getting an exception I am getting a proper datetime 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 datetime instead of throwing an exception.

7
  • why are you waiting for an exception? Commented Sep 28, 2015 at 12:47
  • 4
    @AndrewTobilko, presumably because 31 Feb. doesn't exist. Commented Sep 28, 2015 at 12:49
  • But formatter does not know about that. Commented Sep 28, 2015 at 12:51
  • 4
    @Satya: What makes you think that? Something clearly knows that date doesn't exist, as it's converting it to the 28th... Commented Sep 28, 2015 at 12:56
  • 1
    @JonSkeet My apologies for misleading you earlier, the problem was not the missing milliseconds. It was in fact the missing Era. It still requires a DateTimeFormatterBuilder but with a default for the Era, not for milliseconds. If you want to edit & undelete your answer, feel free. Commented Sep 28, 2015 at 14:29

5 Answers 5

54

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");
        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Change the yyyy to uuuu in format string then there is no need to mess with ERA
11

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);

Comments

3

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.

Comments

2
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

1 Comment

SimpleDateFormat doesn't work with Java 8 LocalDateTime. I am looking for a solution with Java8 LocalDateTime
0

LocalDateTime.parse will only throw an error if the String passed in contains invalid characters, a number of days exceeding 31 or a month exceeding 12.

For example, if you modified your code as such:

String dateString = "11:30:59 0zz2/31/2015";

an exception would be thrown caused by the invalid 'zz' characters within your given date. As to why it's 'rounding-down' the date so to speak, that I don't know.

Source: https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html#parse-java.lang.CharSequence-

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.