Reputation: 51373
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
Reputation: 338775
The accepted Answer by Cayman is correct: leniency in parsing by default is the problem.
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.
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 ) {
…
}
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 ) {
…
}
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 ) {
…
}
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
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
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
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