Why SimpleDateFormat("MM/dd/yyyy") parses date to 10/20/20128?
Asked Answered
A

5

23

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?

Attribute answered 30/8, 2013 at 13:40 Comment(5)
If it were to parse it to 4 digits as opposed to 5, it wouldn't be the correct year.Recitativo
I believe if you are worried about making it 4 incase user input or something is typo'd you would have to check that before setting your date value.Spirt
I don't want it to parse it to 4 digits, I want it to fail (give me an exception) because the value I'm providing doesn't match the exact date format.Attribute
You will have to check the value yourself and catch your own exception in a Try block. Form the answers it seems Java supports years in 2 formats..yy and the full year. Check the date string before assigning it to date.Spirt
It won't. You have to code it. Updated my answer.Uric
U
41

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");            
}
Uric answered 30/8, 2013 at 13:45 Comment(5)
I understand that but the provided value doesn't match the MM/dd/yyyy format I provided so it should still fail.Attribute
@Attribute java interprets anything more than 2 y's as the full year that is supplied. I can't verify but if you did yyy you would still get all 5 numbers.Spirt
@Attribute That's how they decided to implement. Try this: System.out.println(new SimpleDateFormat("yyyyyyyyyy dddddd MMMMMMMM").format(new Date())); And you will get "0000002013 000030 August"Uric
@Attribute If you want to validate, you can create a calendar (say January 01, 2150) and getTimeInMillis() or just hard code and store the value and check if the date you are validating is after the stored date. If it is, it's an invalid date.Uric
If you're writing software that needs to have a 4-digit year, please use "12/31/9999" as your maxDate. We don't need another Y21K problem.Fixing
D
11

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.

Denysedenzil answered 30/8, 2013 at 13:45 Comment(0)
B
7

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.

Brutalize answered 30/8, 2013 at 13:44 Comment(0)
D
1

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.

Desist answered 18/9, 2015 at 11:53 Comment(0)
A
0

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.

Affettuoso answered 5/12, 2021 at 12:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.