Timezones in SQL DATE vs java.sql.Date

后端 未结 7 750
天涯浪人
天涯浪人 2020-11-28 04:11

I\'m getting a bit confused by the behaviour of the SQL DATE data type vs. that of java.sql.Date. Take the following statement, for example:



        
相关标签:
7条回答
  • 2020-11-28 05:18

    JDBC 4.2 and java.time

    PreparedStatement stmt = connection.prepareStatement(sql);
    stmt.setObject(1, LocalDate.EPOCH); // The epoch year LocalDate, '1970-01-01'.
    ResultSet rs = stmt.executeQuery();
    rs.next();
    
    // It doesnt matter where you live or your JVM’s time zone setting
    // since a LocalDate doesn’t have or use time zone
    System.out.println(rs.getObject(1, LocalDate.class));
    

    I didn’t test, but unless there’s a bug in your JDBC driver, the output in all time zones is:

    1970-01-01

    LocalDate is a date without time of day and without time zone. So there is no millisecond value to get and no time of day to be confused about. LocalDate is part of java.time, the modern Java date and time API. JDBC 4.2 specifies that you can transfer java.time objects including LocalDate to and from your SQL database through the methods setObject and getObject I use in the snippet (so not setDate nor getDate).

    I think that virtually all of us are using JDBC 4.2 drivers by now.

    What went wrong in your code?

    java.sql.Date is a hack trying (not very successfully) to disguise a java.util.Date as a date without time of day. I recommend that you don’t use that class.

    From the documentation:

    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.

    “Associated” may be vague. What this means, I believe, is that the millisecond value must denote the start of day in your JVM’s default time zone (Europe/Zurich in your case, I suppose). So when you create new java.sql.Date(0) equal to GMT 1970-01-01 00:00:00, it’s really you creating an incorrect Date object, since this time is 01:00:00 in your time zone. JDBC is ignoring the time of day (we might have expected an error message, but don’t get any). So SQL is getting and returning a date of 1970-01-01. JDBC correctly translates this back to a Date containing CET 1970-01-01 00:00:00. Or a millisecond value of -3 600 000. Yes, it’s confusing. As I said, don’t use that class.

    If you had been at a negative GMT offset (for example most time zones in the Americas), new java.sql.Date(0) would have been interpreted as 1969-12-31, so this would have been the date you had passed to and got back from SQL.

    To make matters worse, think what happens when the JVM’s time zone setting is changed. Any part of your program and any other program running in the same JVM may do this at any time. This typically causes all java.sql.Date objects created before the change to become invalid and may sometimes shift their date by one day (in rare cases by two days).

    Links

    • Oracle tutorial: Date Time explaining how to use java.time.
    • JSR-000221 JDBC™ API Specification 4.2 Maintenance Release 2
    • Documentation of java.sql.Date
    0 讨论(0)
提交回复
热议问题