SimpleDateFormat producing wrong date time when parsing "YYYY-MM-dd HH:mm"
Asked Answered
R

5

22

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()));
Rockaway answered 10/4, 2013 at 4:28 Comment(1)
For the update, java.sql.Date is for a date only, meant for the date datatype of SQL. From the docs: “To conform with the definition of SQL DATE, the millisecond values wrapped by a java.sql.Date instance must be 'normalized' by setting the hours, minutes, seconds, and milliseconds to zero in the particular time zone with which the instance is associated.”Brandes
T
60

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

Tupper answered 10/4, 2013 at 4:30 Comment(2)
Thanks for your answer. I solved the initial problem. When I enter the value into the date base I am getting date correctly but the time always 00:00. ps.setDate(3, new java.sql.Date(app.getDate().getTime()));Rockaway
YOU LITERALLY SAVED MY DAYMihrab
F
8

Use small case Y, not caps. ie yyyy not YYYY

Check the comments here: Java Simple Date Format and other answers referenced there.

Fortyfive answered 10/4, 2013 at 4:54 Comment(0)
I
7

There are two problem.

  1. Format string should be "yyyy-MM-dd HH:mm".
  2. Datatype to store the time is TimeStamp and not Date in database.

Correct both the things and you will be able to store and retrieve Date with time.

Inhospitality answered 10/4, 2013 at 5:30 Comment(0)
B
2

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().

Brandes answered 21/6, 2017 at 19:37 Comment(0)
P
1

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);
  }
}
Proudfoot answered 22/4, 2020 at 9:42 Comment(1)
Hi @Salim. I faced this. I noticed that the parse method was generating wrong outputs, than I saw that it uses instance variables to make its calculations. I was using a static instance.Aggrandize

© 2022 - 2024 — McMap. All rights reserved.