xindanding
xindanding

Reputation: 39

I have a problem about SimpleDateFormat function in java

I don't know why there is no error. The code is

String datestr = "2021-01-01";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
try {
    System.out.println(sdf.parse(datestr));
} catch (ParseException e) {
    e.printStackTrace();
}

Since the date string, 2021-01-01, does not match the format pattern string, yyyy-MM, I had expected and wanted an exception.

The console prints Fri Jan 01 00:00:00 CST 2021.

The opposite situation does result in an exception as expected. I make code look like this

        String datestr = "2021-01";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        try {
            System.out.println(sdf.parse(datestr));
        } catch (ParseException e) {
            e.printStackTrace();
        }

The console prints an error msg: Unparseable date: "2021-01"

Who can tell me why?

Upvotes: 0

Views: 576

Answers (4)

jtahlborn
jtahlborn

Reputation: 53694

Others have already mentioned why this happens (the java parsers stop parsing when they get all they need) and have also explained that this API is older and not as ergonomic. That said, you can do what you want using the ParsePosition class.

String datestr = "2021-01-01";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
ParsePosition pp = new ParsePosition(0);
sdf.parse(datestr, pp);
if(pp.getIndex() < dateStr.length()) {
  throw new ParseException("Parsing failed...");
}

Upvotes: 1

Anonymous
Anonymous

Reputation: 86286

Here’s a modest supplement. Basil Bourque has already shown the good and modern solution to the work you are doing, and Generous Badger has explained why your code didn’t behave the way you had expected.

The code by Basil Bourque does throw the expected exception if you feed a string in the wrong format into each of the code examples. For the modern version of the code you were asking about:

    String datestr = "2021-01-01";
    YearMonth.parse(datestr);

The result is an:

Exception in thread "main" java.time.format.DateTimeParseException: Text '2021-01-01' could not be parsed, unparsed text found at index 7

Please also enjoy how precise the exception message is.

The same thing happens if you choose to use an explicit formatter:

    YearMonth.parse(datestr, DateTimeFormatter.ofPattern("uuuu-MM"));

Exception in thread "main" java.time.format.DateTimeParseException: Text '2021-01-01' could not be parsed, unparsed text found at index 7

Don’t use SimpleDateFormat

There are a lot of confusing and ill-designed traits of SimpleDateFormat, the class that you tried to use in your two code examples. You have been hit by just of two of them:

  1. As Generous Badger explained, it may not parse the entire string given to it, tacitly leaving unparsed text.
  2. It invents default values for the fields not parsed. In your case it defaulted to the 1st of the month 00:00:00 in the default time zone of the JVM. While java.time can be instructed to apply default values for fields that are not in the parsed string, this requires specifying them explicitly in the Java code, leading to much more predictable and less surprising behaviour.

Upvotes: 1

Basil Bourque
Basil Bourque

Reputation: 338740

tl;dr

Your formatting pattern must fit your input text. In the first case, "2021-01-01" does not match "yyyy-MM". In the second case, "2021-01" does not match "yyyy-MM-dd".

Use java.time classes instead, as wrestling with legacy classes SimpleDateFormat & Date is unwise. Simpler too, with no need of formatting patterns.

YearMonth
.from( 
    LocalDate.parse( "2021-01-01" ) 
)
.toString()

See this code run live at IdeOne.com.

2021-01

And parsing year-month:

YearMonth.parse( "2021-01" )

Details

You are using terrible date-time classes that were years ago supplanted by the modern java.time classes. Sun, Oracle, and the JCP community gave up on those closes with the unanimous adoption of JSR 310.

Your inputs happen to use standard ISO 8601 formats. The java.time classes use these formats by default when parsing/generating text. So no need to specify a formatting pattern.

LocalDate ld = LocalDate.parse( "2021-01-01" ) ;
YearMonth ym = YearMonth.from( ld ) ;
String output = ym.toString() ;

As for parsing an input of "2021-01":

YearMonth ym = YearMonth.parse( "2021-01" ) ;

Upvotes: 2

Generous Badger
Generous Badger

Reputation: 421

You are describing two different scenarios:

  1. Your input text contains more data than the pattern would consume and the extra data is at the end.
  2. Your input text contains less data than the pattern would consume.

#1 is not an error, since parse(String) is explicitly documented to just read from the beginning of the input until it has enough:

The method may not use the entire text of the given string.

#2 is an error, because you the pattern says it expects extra data (namely a - followed by a day) and that data is missing.

Upvotes: 2

Related Questions