ChatGPT解决这个技术问题 Extra ChatGPT

java.util.Date vs java.sql.Date

java.util.Date vs java.sql.Date: when to use which and why?


i
indivisible

Congratulations, you've hit my favorite pet peeve with JDBC: Date class handling.

Basically databases usually support at least three forms of datetime fields which are date, time and timestamp. Each of these have a corresponding class in JDBC and each of them extend java.util.Date. Quick semantics of each of these three are the following:

java.sql.Date corresponds to SQL DATE which means it stores years, months and days while hour, minute, second and millisecond are ignored. Additionally sql.Date isn't tied to timezones.

java.sql.Time corresponds to SQL TIME and as should be obvious, only contains information about hour, minutes, seconds and milliseconds.

java.sql.Timestamp corresponds to SQL TIMESTAMP which is exact date to the nanosecond (note that util.Date only supports milliseconds!) with customizable precision.

One of the most common bugs when using JDBC drivers in relation to these three types is that the types are handled incorrectly. This means that sql.Date is timezone specific, sql.Time contains current year, month and day et cetera et cetera.

Finally: Which one to use?

Depends on the SQL type of the field, really. PreparedStatement has setters for all three values, #setDate() being the one for sql.Date, #setTime() for sql.Time and #setTimestamp() for sql.Timestamp.

Do note that if you use ps.setObject(fieldIndex, utilDateObject); you can actually give a normal util.Date to most JDBC drivers which will happily devour it as if it was of the correct type but when you request the data afterwards, you may notice that you're actually missing stuff.

I'm really saying that none of the Dates should be used at all.

What I am saying that save the milliseconds/nanoseconds as plain longs and convert them to whatever objects you are using (obligatory joda-time plug). One hacky way which can be done is to store the date component as one long and time component as another, for example right now would be 20100221 and 154536123. These magic numbers can be used in SQL queries and will be portable from database to another and will let you avoid this part of JDBC/Java Date API:s entirely.


Nice answer. But isn't storing dates as longs a bit unfriendly for the DBA?
Perhaps, however DBA:s generally tend to their RDBMS of choice and rejecting everything that isn't about that RDBMS directly (I'm looking at you, Oracle fans) while Java applications are expected to work with all of them. Personally I don't like to put my logic into DB at all.
My mysql column is a datetime, but doing ps.setDate(new java.sql.Date(myObject.getCreatedDate().getTime())); I am loosing the milliseconds portion, how to fix this?
To not lose your milliseconds: new java.sql.Timestamp( utilDate.getTime() )
I mentioned that it's a common bug that it is TZ specific while it by specification shouldn't be.
C
Community

LATE EDIT: Starting with Java 8 you should use neither java.util.Date nor java.sql.Date if you can at all avoid it, and instead prefer using the java.time package (based on Joda) rather than anything else. If you're not on Java 8, here's the original response:

java.sql.Date - when you call methods/constructors of libraries that use it (like JDBC). Not otherwise. You don't want to introduce dependencies to the database libraries for applications/modules that don't explicitly deal with JDBC.

java.util.Date - when using libraries that use it. Otherwise, as little as possible, for several reasons:

It's mutable, which means you have to make a defensive copy of it every time you pass it to or return it from a method.

It doesn't handle dates very well, which backwards people like yours truly, think date handling classes should.

Now, because j.u.D doesn't do it's job very well, the ghastly Calendar classes were introduced. They are also mutable, and awful to work with, and should be avoided if you don't have any choice.

There are better alternatives, like the Joda Time API (which might even make it into Java 7 and become the new official date handling API - a quick search says it won't).

If you feel it's overkill to introduce a new dependency like Joda, longs aren't all that bad to use for timestamp fields in objects, although I myself usually wrap them in j.u.D when passing them around, for type safety and as documentation.


Why should we prefer java.time over java.sql when storing in database? I'm really interesting in the statement, but I want to understand why :)
@Jean-FrançoisSavard I hope you have found an answer for your question since you posted that comment - but here's an answer, just for completeness' sake: It's still perfectly OK to java.sql.Date with PreparedStatement etc! But when you're passing it around, use a LocalDate which you convert using java.sql.Date.valueOf and java.sql.Date.valueOf when setting it, and convert it back as early as possible using java.sql.Date.toLocalDate - again, because you want to involve java.sql as little as possible, and because it's mutable.
s
saravanakumar

The only time to use java.sql.Date is in a PreparedStatement.setDate. Otherwise, use java.util.Date. It's telling that ResultSet.getDate returns a java.sql.Date but it can be assigned directly to a java.util.Date.


Ehm, ResultSet#getDate() returns sql.Date (which extends util.Date).
@Esko - "Ehm", I fixed that before you commented (and downvoted).
It's important to note that the reason a java.sql.Date can be assigned to a java.util.Date is because the first is a subclass of the second.
Why is it 'telling' that a java.sql.Date can be assigned to a java.util.Date when the former extends the latter? What's the point you're trying to make?
B
Basil Bourque

tl;dr

Use neither.

java.time.Instant replaces java.util.Date

java.time.LocalDate replaces java.sql.Date

Neither

java.util.Date vs java.sql.Date: when to use which and why?

Both of these classes are terrible, flawed in design and in implementation. Avoid like the Plague Coronavirus.

Instead use java.time classes, defined in in JSR 310. These classes are an industry-leading framework for working with date-time handling. These supplant entirely the bloody awful legacy classes such as Date, Calendar, SimpleDateFormat, and such.

java.util.Date

The first, java.util.Date is meant to represent a moment in UTC, meaning an offset from UTC of zero hours-minutes-seconds.

java.time.Instant

Now replaced by java.time.Instant.

Instant instant = Instant.now() ;  // Capture the current moment as seen in UTC.

java.time.OffsetDateTime

Instant is the basic building-block class of java.time. For more flexibility, use OffsetDateTime set to ZoneOffset.UTC for the same purpose: representing a moment in UTC.

OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;

You can send this object to a database by using PreparedStatement::setObject with JDBC 4.2 or later.

myPreparedStatement.setObject( … , odt ) ;

Retrieve.

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

java.sql.Date

The java.sql.Date class is also terrible and obsolete.

This class is meant to represent a date only, without a time-of-day and without a time zone. Unfortunately, in a terrible hack of a design, this class inherits from java.util.Date which represents a moment (a date with time-of-day in UTC). So this class is merely pretending to be date-only, while actually carrying a time-of-day and implicit offset of UTC. This causes so much confusion. Never use this class.

java.time.LocalDate

Instead, use java.time.LocalDate to track just a date (year, month, day-of-month) without any time-of-day nor any time zone or offset.

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
LocalDate ld = LocalDate.now( z ) ;    // Capture the current date as seen in the wall-clock time used by the people of a particular region (a time zone).

Send to the database.

myPreparedStatement.setObject( … , ld ) ;

Retrieve.

LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;

https://i.stack.imgur.com/UmyHG.png

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation. Java 9 adds some minor features and fixes.

Java 9 adds some minor features and fixes.

Java SE 6 and Java SE 7 Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.

Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.

Android Later versions of Android bundle implementations of the java.time classes. For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….

Later versions of Android bundle implementations of the java.time classes.

For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….

https://i.stack.imgur.com/ImlbG.png


Upvote for replacing the Plague reference by Corona virus. More relatable now.
Hi @Basil. I have to use IBM's ICU("have to" : as far as my knowledge says) because i'm using Iranian calendar(some say Jalali calendar). IBM's ICU uses notorious(maybe strong word) java.util.Date api. after reading your detailed answers, i decided to use java.time but i have to convert them all. just a few classes. i wrote a converter using java.time.Instant. Is there a good solution to my problem(If it's a problem. Actually i need your guide on this; maybe i don't know where the problem is)?
I almost forgot to thank you for your answer. Thank you.
@Arash As for converting between the legacy date-time classes and java.time, yes, that is easy and clean. Look for new conversion methods added to the old classes. Look for to…/from… methods, such as java.util.Date.from( Instant ). java.util.Date maps to Instant, GregorianCalendar maps to ZonedDateTime, java.sql.Date to LocalDate, and so on.
I
Israelm

I had the same issue, the easiest way i found to insert the current date into a prepared statement is this one:

preparedStatement.setDate(1, new java.sql.Date(new java.util.Date().getTime()));

cant get timezone with this.
K
Kent Tong

The java.util.Date class in Java represents a particular moment in time (e,.g., 2013 Nov 25 16:30:45 down to milliseconds), but the DATE data type in the DB represents a date only (e.g., 2013 Nov 25). To prevent you from providing a java.util.Date object to the DB by mistake, Java doesn’t allow you to set a SQL parameter to java.util.Date directly:

PreparedStatement st = ...
java.util.Date d = ...
st.setDate(1, d); //will not work

But it still allows you to do that by force/intention (then hours and minutes will be ignored by the DB driver). This is done with the java.sql.Date class:

PreparedStatement st = ...
java.util.Date d = ...
st.setDate(1, new java.sql.Date(d.getTime())); //will work

A java.sql.Date object can store a moment in time (so that it’s easy to construct from a java.util.Date) but will throw an exception if you try to ask it for the hours (to enforce its concept of being a date only). The DB driver is expected to recognize this class and just use 0 for the hours. Try this:

public static void main(String[] args) {
  java.util.Date d1 = new java.util.Date(12345);//ms since 1970 Jan 1 midnight
  java.sql.Date d2 = new java.sql.Date(12345);
  System.out.println(d1.getHours());
  System.out.println(d2.getHours());
}

A
Abdul Alim Shakir

java.util.Date represents a specific instant in time with millisecond precision. It represents both date and time information without timezone. The java.util.Date class implements Serializable, Cloneable and Comparable interface. It is inherited by java.sql.Date, java.sql.Time and java.sql.Timestamp interfaces.

java.sql.Date extends java.util.Date class which represents date without time information and it should be used only when dealing with databases. 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.

It inherits all public methods of java.util.Date such as getHours(), getMinutes(), getSeconds(), setHours(), setMinutes(), setSeconds(). As java.sql.Date does not store the time information, it override all the time operations from java.util.Dateand all of these methods throw java.lang.IllegalArgumentException if invoked as evident from their implementation details.