Question

I have this code:

  DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
  dateFormat.setLenient(false);
  Date date = dateFormat.parse("10/20/20128");

and I would expect the dateFormat.parse call to throw ParseException since the year I'm providing is 5 characters long instead of 4 like in the format I defined. But for some reason even with the lenient set to false this call returns a Date object of 10/20/20128.

Why is that? It doesn't make much sense to me. Is there another setting to make it even more strict?

Was it helpful?

Solution

20128 is a valid year and Java hopes the world to live that long I guess.

if the number of pattern letters is more than 2, the year is interpreted literally, regardless of the number of digits.

Reference.

If you want to validate if a date is in limit, you can define one and check-

SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
Date maxDate = sdf.parse("01/01/2099");  // This is the limit

if(someDate.after(maxDate)){            
    System.out.println("Invalid date");            
}

OTHER TIPS

See the javadoc

Year: If the formatter's Calendar is the Gregorian calendar, the following rules are applied.

  • For formatting, if the number of pattern letters is 2, the year is truncated to 2 digits; otherwise it is interpreted as a number.
  • For parsing, if the number of pattern letters is more than 2, the year is interpreted literally, regardless of the number of digits. So using the pattern "MM/dd/yyyy", "01/11/12" parses to Jan 11, 12 A.D.
  • For parsing with the abbreviated year pattern ("y" or "yy"), SimpleDateFormat must interpret the abbreviated year relative to some century. It does this by adjusting dates to be within 80 years before and 20 years after the time the SimpleDateFormat instance is created. For example, using a pattern of "MM/dd/yy" and a SimpleDateFormat
    instance created on Jan 1, 1997, the string "01/11/12" would be
    interpreted as Jan 11, 2012 while the string "05/04/64" would be
    interpreted as May 4, 1964. During parsing, only strings consisting
    of exactly two digits, as defined by Character.isDigit(char), will be parsed into the default century. Any other numeric string, such as a
    one digit string, a three or more digit string, or a two digit string that isn't all digits (for example, "-1"), is interpreted literally.
    So "01/02/3" or "01/02/003" are parsed, using the same pattern, as
    Jan 2, 3 AD. Likewise, "01/02/-3" is parsed as Jan 2, 4 BC.
  • Otherwise, calendar system specific forms are applied. For both formatting and parsing, if the number of pattern letters is 4 or
    more, a calendar specific long form is used. Otherwise, a calendar
    specific short or abbreviated form is used.

Therefore, it will read all the characters that come after the last / as the year.

See java.text.SimpleDateFormat API, pattern letter y: For parsing, if the number of pattern letters is more than 2, the year is interpreted literally, regardless of the number of digits.

Using SimpleDateFormat in java, you can achieve a number of human readable formats. Consider this code snippet:

        Date curDate = new Date();

        SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");

        String DateToStr = format.format(curDate);
        System.out.println(DateToStr);

        format = new SimpleDateFormat("dd-M-yyyy hh:mm:ss");
        DateToStr = format.format(curDate);
        System.out.println(DateToStr);

        format = new SimpleDateFormat("dd MMMM yyyy zzzz", Locale.ENGLISH);
        DateToStr = format.format(curDate);
        System.out.println(DateToStr);

        format = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z");
        DateToStr = format.format(curDate);
        System.out.println(DateToStr);

Look at the various outputs generated by the various formats:

2014/05/11

11-5-2014 11:11:51

11 May 2014 Eastern European Summer Time

Sun, 11 May 2014 23:11:51 EEST

Feel free to modify the format string and you might get the desired result.

java.time

I always recommend that you use java.time, the modern Java date and time API, for your date work. In addition it so happens that java.time will give you the exception you asked for.

    DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM/dd/uuuu")
            .withResolverStyle(ResolverStyle.LENIENT);
    LocalDate date = LocalDate.parse("10/20/20128", dateFormatter);

Result:

Exception in thread "main" java.time.format.DateTimeParseException: Text '10/20/20128' could not be parsed at index 6

The mentioned index 6 is where the 5-digit year is in your string.

This said, the range check suggested by others could still be a good idea. With java.time this is easy. Say for example that we don’t want to accept any future date:

    LocalDate date = LocalDate.parse("10/20/8012", dateFormatter);
    if (date.isAfter(LocalDate.now(ZoneId.systemDefault()))) {
        System.err.println("Date must not be in the future, was " + date);
    }

Date must not be in the future, was 8012-10-20

Tutorial link: Trail: Date Time (The Java™ Tutorials) explaining how to use java.time.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top