René Link
René Link

Reputation: 51373

How to prevent SimpleDateFormat to parse wrong formatted dates?

I use SimpleDateFormat to parse strings to Date objects and I wonder why the results are not what I expect.

For example:

DateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");

Date date = yyyyMMdd.parse("20100725");
System.out.println(date);

works as expected and outputs

Sun Jul 25 00:00:00 CEST 2010

But

Date date = yyyyMMdd.parse("2010-07-25");
System.out.println(date);

also works and outputs

Mon Dec 07 00:00:00 CET 2009

I expected a ParseException, but it seems that SimpleDateFormat interpretes the month part -07 and the day part -25 as a negative number. First I couldn't figure out how it comes to 7th of december. So I tried another value:

Date date = yyyyMMdd.parse("2010-7-25");
System.out.println(date);

and it outpus

Sun Apr 05 00:00:00 CEST 2009

So it seems that it somehow subtracts 7 month from the year 2010 which whould be 1th of may, and 25 days so the result is 5th of april 2009.

Image that you use the pattern yyyyMMdd in an service implementation and some client accidentially sends the date as yyyy-MM-dd. You will not get an exception. Instead you will get totally different dates. I guess this is not what you expect.

E.g.

String clientData = "2010-05-23";

DateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
Date parsedDate = yyyyMMdd.parse(clientData);

System.out.println("Client  : " + clientData);
System.out.println("Service : " + yyyyMMdd.format(parsedDate));

Do I miss something?

How do I prevent SimpleDateFormat to parse 'wrong' dates?

Sure I can use a regular expression to check first, but is there a better way?

Upvotes: 2

Views: 2349

Answers (4)

Basil Bourque
Basil Bourque

Reputation: 338775

The accepted Answer by Cayman is correct: leniency in parsing by default is the problem.

java.time

You are using troublesome old date-time classes now supplanted by the java.time classes.

No such leniency-by-default problem in java.time. If the input does not strictly match the formatting pattern, a DateTimeParseException is thrown.

The LocalDate class represents a date-only value without time-of-day and without time zone.

ISO 8601 format

For standard ISO 8601 formatted inputs of YYYY-MM-DD, simply call parse directly.

String input = "2010-05-23";
try {
    LocalDate  ld = LocalDate.parse( input ); // Expects standard ISO 8601 input format.
} catch ( DateTimeParseException e ) {
    …
}

“Basic” ISO 8601 format

The ISO 8601 standard allows for “basic” formats that minimize the use of separators. Not that I recommend these variations, but they exist.

Currently java.time predefines only a single one of these “basic” variations, DateTimeFormatter.BASIC_ISO_DATE.

String input = "20100725";
try {
    LocalDate  ld = LocalDate.parse( input , DateTimeFormatter.BASIC_ISO_DATE ); 
} catch ( DateTimeParseException e ) {
    …
}

Custom format

For other formats, specify a formatter.

String input = "2010/07/25";
try {
    DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu/MM/dd" );
    LocalDate  ld = LocalDate.parse( input , f ); // Custom format.
} catch ( DateTimeParseException e ) {
    …
}

Localized format

Or let java.time determine the localized format.

String input = … ;
try {
    Locale l = Locale.CANADA_FRENCH ; 
    DateTimeFormatter f = DateTimeFormatter.ofLocalizedDate( FormatStyle.MEDIUM ).withLocale( l );
    LocalDate  ld = LocalDate.parse( input , f ); // Localized format.
} catch ( DateTimeParseException e ) {
    …
}

Upvotes: 3

Kayaman
Kayaman

Reputation: 73558

Use SimpleDateFormat.setLenient(false); to get an exception. Otherwise it will try to parse the input as best as it can, which is usually wrong.

For some reason they decided that leniency should be true by default, but that is hardly a surprise.

Specify whether or not date/time parsing is to be lenient. With lenient parsing, the parser may use heuristics to interpret inputs that do not precisely match this object's format. With strict parsing, inputs must match this object's format.

Upvotes: 8

Arnav Borborah
Arnav Borborah

Reputation: 11789

SimpleDateFormat.setLenient(false);

Is what needs to be done, or the input will be tried to be parsed well, and as you know, that doesn't always work. With the function above, the compiler will be strict about the format.

Upvotes: 2

Michael Gantman
Michael Gantman

Reputation: 7790

First, if you want to parse String "2010-05-23" your mask should be "yyyy-MM-dd" and not "yyyyMMdd". Second SimpleDateFormat has serious problems as it is not Thread safe. If you use java 8 then use learn and use new package "java.time". If you use any java earlier then version 8 then use some other frameworks for parsing date. One of the most popular is Joda time. Works much better.

Upvotes: 0

Related Questions