SimpleDateFormat producing wrong date time when parsing "YYYY-MM-dd HH:mm"
I am trying to parse a String
(YYYY-MM-dd HH:mm
) to Date
, however getting wrong date than expected.
CODE:
Date newDate = null;
String dateTime = "2013-03-18 08:30";
SimpleDateFormat df = new SimpleDateFormat("YYYY-MM-dd HH:mm", Locale.ENGLISH);
df.setLenient(false);
try {
newDate = df.parse(dateTime);
} catch (ParseException e) {
throw new InvalidInputException("Invalid date input.");
}
Produces:
Sun Dec 30 08:30:00 EST 2012 (wrong)
I tried setting Lenient off but no luck.
Update
Thanks Sudhanshu for the answer, it helped me to solve the Java conversion. When I enter the returned date from the above code into the database I am getting the date correctly but the time is always 00:00
.
ps.setDate(3, new java.sql.Date(app.getDate().getTime()));
YYYY should be yyyy-
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ENGLISH);
Please check the documentation for SimpleDateFormat here
Java 6 : http://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html
Java 7 : http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
Use small case Y, not caps. ie yyyy not YYYY
Check the comments here: Java Simple Date Format and other answers referenced there.
There are two problem.
- Format string should be
"yyyy-MM-dd HH:mm"
. - Datatype to store the time is
TimeStamp
and notDate
in database.
Correct both the things and you will be able to store and retrieve Date with time.
Here’s the modern answer. The other answers were fine answers when they were written in 2013. The year after that the modern date and time API came out as a replacement for the old classes Date
, SimpleDateFormat
and friends. IMHO you should use the new classes now. They are much more programmer friendly.
LocalDateTime newDate = null;
String dateTime = "2013-03-18 08:30";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm", Locale.ENGLISH);
try {
newDate = LocalDateTime.parse(dateTime, dtf);
} catch (DateTimeParseException e) {
throw new InvalidInputException("Invalid date input.");
}
Except for the class names it’s not that different from the old code. With other examples there will be differences, typically the modern classes will provide for clearer code and fewer opportunities for errors.
For just one little demonstration of a difference, let’s try the format pattern string you had with uppercase YYYY
in it:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm", Locale.ENGLISH);
Now the code throws a java.time.format.DateTimeParseException: Text '2013-03-18 08:30' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {MonthOfYear=3, DayOfMonth=18, WeekBasedYear[WeekFields[SUNDAY,1]]=2013},ISO resolved to 08:30 of type java.time.format.Parsed
. It’s long, I know. The thing to note is that there is no year among the fields it mentions, only a WeekBasedYear. It’s not the same; and you may have figured out by now that this is exactly because uppercase Y
is for week-based year (only usseful with a week number), where you should use lowercase y
for year. I consider this behaviour a bit more helpful than what the old classes did: they gave you a result that was wrong, pretended everything was fine and left you completely in the dark about what was wrong.
Next I understand that you want to store your date-time to a database. In the old days you would convert to some appropriate java.sql
type. No longer necessary. I believe with a JDBC 4.2 driver you can just do:
ps.setObject(3, newDate);
I have not tested this with a database, though. Note that you use setObject()
instead of setDate()
.
Although this was not the case here, beware that SimpleDateFormat
is not thread-safe. So when used in a multi-threaded environment, race conditions can happen and cause wrong parsing results. Solution is to either
- create a new instance on each usage (might decrease performance)
- instantiate once but use it in a
synchronized
block - instantiate once but use
ThreadLocal
Example with ThreadLocal
:
class MyClass {
private ThreadLocal<SimpleDateFormat> threadLocalFormat =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ENGLISH));
public void foo(String bar) {
return threadLocalFormat.get().parse(bar);
}
}