21

Should I use java.util.Date or java.sql.Date?

I have a VisualFox database and I have retrieved the entities with the IntelliJ Idea wizard using an appropiate jdbc type 4 driver.

The ide (or the driver) has created the date fields as Timestamp. However, the date fields are not timestamps but Date fields, they store year, month and day only.

So I wonder if I should switch to java.util.Date or java.sql.Date. At first glance I thought that java.sql.Date should be the appropiate one, but it has many methods declared as deprecated.

Carlos Goce
  • 1,625
  • 4
  • 22
  • 33

4 Answers4

44

tl;dr

Should I use java.util.Date or java.sql.Date?

Neither.

Both are obsolete as of JDBC 4.2 and later. Use java.time classes instead.

  • date-only value
    For a database type akin to SQL-standard DATE, use java.time.LocalDate.
    • LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;
    • myPreparedStatement.setObject( ld , … ) ;
  • date with time-of-day in UTC value
    For a database type akin to SQL-standard TIMESTAMP WITH TIME ZONE, use java.time.Instant.
    • Instant instant = myResultSet.getObject( … , Instant.class ) ;
    • myPreparedStatement.setObject( instant , … ) ;

Details

The question and other answers seem to be over-thinking the issue. A java.sql.Date is merely a java.util.Date with its time set to 00:00:00.

From the java.sql.Date doc (italicized text is mine)…

Class Date

java.lang.Object

    java.util.Date        ← Inherits from j.u.Date

        java.sql.Date

A thin wrapper around a millisecond value that allows JDBC to identify this as an SQL DATE value. A milliseconds value represents the number of milliseconds that have passed since January 1, 1970 00:00:00.000 GMT.  ← Time-of-day set to Zero, midnight GMT/UTC

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.

Date-Only versus Date-Time

The core problem is:

  • SQL
    In SQL, the DATE data type stores a date-only, without a time-of-day.
  • JAVA
    In the badly designed date-time library bundled with the early versions of Java, they failed to include a class to represent a date-only.

Instead of creating a date-only class, the Java team made a terrible hack. They took their date-time class (the misnamed java.util.Date class, containing both date and time) and extended it to have an instance set its time-of-day to midnight UTC, 00:00:00. That hack, that subclass of j.u.Date, is java.sql.Date.

All this hacking, poor design, and misnaming has made a confusing mess.

Which To Use

So when to use which? Simple, after cutting through the confusion.

  • When reading or writing to a database’s date-only column, use java.sql.Date as it clumsily tries to mask its time-of-day.
  • Everywhere else in Java, where you need a time-of-day along with your date, use java.util.Date.
  • When you have a java.sql.Date in hand but need a java.util.Date, simply pass the java.sql.Date. As a subclass, a java.sql.Date is a java.util.Date.

Even Better

In modern Java, you now have a choice of decent date-time libraries to supplant the old and notoriously troublesome java.util.Date, Calendar, SimpleTextFormat, and java.sql.Date classes bundled with Java. The main choices are:

Both offer a LocalDate class to represent a date only, with no time-of-day and no time zone.

A JDBC driver updated to JDBC 4.2 or later can be used to directly exchange java.time objects with the database. Then we can completely abandon the ugly mess that is the date-time classes in the java.util.* and java.sql.* packages.

setObject | getObject

This article published by Oracle explains that the JDBC in Java 8 has been updated transparently to map a SQL DATE value to the new java.time.LocalDate type if you call getObject and setObject methods.

In obtuse language, the bottom of the JDBC 4.2 update spec confirms that article, with new mappings added to the getObject and setObject methods.

myPreparedStatement.setObject( … , myLocalDate ) ;

…and…

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

Convert

The spec also says new methods have been added to the java.sql.Date class to convert back and forth to java.time.LocalDate.

Time Zone

The old java.util.Date, java.sql.Date, and java.sql.Timestamp are always in UTC. The first two (at least) have a time zone buried deep in their source code but is used only under-the-surface such as the equals method, and has no getter/setter.

More confusingly, their toString methods apply the JVM’s current default time zone. So to the naïve programmer it seems like they have a time zone but they do not.

Both the buried time zone and the toString behavior are two of many reasons to avoid these troublesome old legacy classes.

Write your business logic using java.time (Java 8 and later). Where java.time lacks, use Joda-Time. Both java.time and Joda-Time have convenient methods for going back and forth with the old classes where need be.

Replacements:

The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).

All three java.time.Local… classes are all lacking any concept of time zone or offset-from-UTC.


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.

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

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

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?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Finally I used the Joda-Time library which is more easier and simpler. Thank You – Carlos Goce Jul 10 '14 at 07:37
  • I'm not sure that the `instance set its time-of-day to midnight UTC`. At least with EclipseLink JPA implementation, the instance will set its time according to the local JVM timezone. So if you read `11/5/2015` from the database into `java.sql.Date` instance, and then apply `getTime` on this instance, you will different results on different timezones. – gamliela May 11 '15 at 13:27
  • @gamliela Read the section, "Time Zone", newly added. I suspect you are confused about the awkward time zone behavior in Java's old date-time classes. Also, read the doc, newly quoted in my answer above: A java.sql.Date is indeed merely a java.util.Date with its time set to zero (midnight UTC/GMT). – Basil Bourque May 11 '15 at 15:32
  • The timezone is buired in source code as you said, and as long as you avoid deprecated functions of `java.util.date` it shouldn't cause any problem - I agree. But I think that some JPA/JDBC implementations actually use these deprecated functions and that's where you might encounter problems. See the discussion [here](http://stackoverflow.com/questions/9202857/timezones-in-sql-date-vs-java-sql-date), it's a real problem. The bottom line is that if you use `java.sql.date`, make sure you don't apply `getTime()`, or set your JVM timezone to UTC. – gamliela May 11 '15 at 15:59
  • I will basically repeat what @gamliela said, and suggest to clarify your answer accordingly. JDBC doesn't create a `java.sql.Date` where the time part is 00:00 when rendered using UTC. Instead, it creates one where the time part is 00:00 when rendered in the default time zone of the JVM (or, AFAIR of the JDBC connection, if you care to change its time zone from its default). `java.sql.Time` follows the same logic; it looks as intended if it's rendered using the default time zone of the JVM. – ddekany Dec 22 '17 at 20:31
  • I also don't follow the addition about "old date-time classes". `java.util.Date` needs no time zone, as it's "physical" time, independent of where you are. As such it can be rendered in any time zone, and it will look differently in each. There's nothing fundamentally wrong with its `toString` (though an ISO datetime with a zone offset would be arguably more helpful). The problem is that date and time values in databases are usually just a bunch of fields (Y, M, D and such) that can't be translated to an instant on the "physical" timeline, hence the workaround in JDBC with the JVM timezone. – ddekany Dec 22 '17 at 20:37
  • @ddekany No, you are incorrect: **A `java.sql.Date` is indeed in GMT** (UTC practically speaking). It says so right *in the documentation I quoted*! I suggest you carefully re-read my Answer. A `java.sql.Date` is technically a subclass of `java.util.Date`, though the doc advises us to pretend otherwise. The JVM’s current default time zone is irrelevant to the value of the `java.sql.Date`. All of this points to well-intentioned but unwise design decisions by the early Java team. Thankfully, we now have *java.time* classes and can forget about these troublesome old legacy classes. – Basil Bourque Dec 23 '17 at 20:57
  • @ddekany Regarding your quote: "it's "physical" time, independent of where you are". I do not know what you mean. There is no such thing. We can only measure time by a reference, and that reference nowadays is commonly UTC. A `java.util.Date` tracks time as a count of milliseconds from first moment of 1970 in UTC. – Basil Bourque Dec 23 '17 at 21:08
  • @ddekany RE your quote: "date and time values in databases are usually just a bunch of fields (Y, M, D and such) that can't be translated to an instant on the "physical" timeline,". Simply not true as to how serious databases typically store date-times. And it is not true that we cannot get moments on the timeline from a database - frankly this statement and your "workaround in JDBC" are nonsensical or I miss your point. `myResultSet.getObject(…,Instant.class)` I suggest more study of how date-time handling in done. Read more Stack Overflow & open source code, and ignore legacy Java classes. – Basil Bourque Dec 23 '17 at 21:18
  • @BasilBourque Even the documentation you have quoted says that the time part of `java.sql.Date` is "zero in the *particular time zone* with which the instance is associated", not in the UTC time zone. But if you still don't believe me, try how JDBC drivers work in reality yourself. (AFAR I have tested it like 2 years ago with MySQL, Oracle and PostgreSQL as well. I have also tested it with PostgreSQL right now.) – ddekany Dec 24 '17 at 07:56
  • @BasilBourque: I did not mean to say that databases can't store point in time, but that they are often (I would risk, typically) store date-only and time-only values like that "bunch of fields". (Especially, date-only values often has no "with time zone" variant.) Anyway, JDBC had to be prepared to that when returning those values as `Date`-s. I'm not sure what's nonsensical about the "workaround in JDBC"... it's just how it works. – ddekany Dec 24 '17 at 08:01
  • calendar very much has a timeZone – Kalpesh Soni Jun 07 '18 at 19:13
  • @KalpeshSoni ?? – Basil Bourque Jun 07 '18 at 20:51
  • if you have java 8 you can use java.time, before that Calendar worked for most cases - you said they have no timezone ! – Kalpesh Soni Jun 12 '18 at 20:21
  • I know it is complicated, even more when you consider how Oracle behaves but - https://stackoverflow.com/questions/14070572/is-java-sql-timestamp-timezone-specific – Kalpesh Soni Jun 12 '18 at 20:23
  • @KalpeshSoni Can you point out where I said the `java.util.Calendar` class has no time zone? Either you misread me or I miscommunicated. Beyond that, there is no reason to ever use the `Date`, `Calendar`, or `Timestamp` classes again. Now supplanted entirely by the *java.time* classes built into Java 8 and later. For Java 6 & 7, use the *ThreeTen-Backport* project where much of the *java.time* functionality has been back-ported. – Basil Bourque Jun 12 '18 at 20:32
  • The old date-time classes in Java (java.util.Date/.'Calendar', java.sql.Date/.Timestamp, and so on, before java.time) have no time zone practically speaking – Kalpesh Soni Jun 12 '18 at 21:26
  • if you are dealing with java 7 and jdbc, you dont have luxury of using joda time or java 8 apis – Kalpesh Soni Jun 12 '18 at 21:26
  • 1
    As for Java 7 and JDBC, re-read my last comment above. And I edited my Answer to post the same info. – Basil Bourque Jun 12 '18 at 22:30
  • @KalpeshSoni As for `Calendar` and time zone, my writing was incorrect. Fixed. Thank you. – Basil Bourque Jun 12 '18 at 23:15
3

Well, according to this article you can use javax.sql.Date without @Temporal annotation which can save some coding from you. However java.util.Date is easier to use across your whole application.

So I would use

@Column(name = "date")
@Temporal(TemporalType.DATE)
private java.util.Date date;
Petr Mensik
  • 26,874
  • 17
  • 90
  • 115
  • well, yes ... but it is "java.sql.Date" and "java.sql.*". It is not a javax package – Neil Stockton Jul 09 '14 at 12:28
  • @PetrMensik Incorrect, you do *not* have to convert a java.sql.Date object to a java.util.Date. A java.sql.Date *is* a java.util.Date, so you can simply pass one anywhere a java.util.Date is expected. – Basil Bourque Jul 09 '14 at 18:38
  • Note that even if you declare that field as `java.util.Date`, you will probably get a `java.sql.Date` value. At least with Hibernate 5 + PostgreSQL 9 it's surely happens like that. So you might as well just declare the field as `java.sql.Date`... you end up with the same actual value in both cases, and at least it's more obvious when someone reads the code. – ddekany Dec 22 '17 at 20:44
2

In general, I find it advisable to use the java.util.Date, since you can use it anywhere in your program without either converting the type or polluting your application with SQL-specific code.

I am not aware of a scenario where 'java.sql.Date' would be a better fit.

kostja
  • 60,521
  • 48
  • 179
  • 224
  • 1
    `java.sql.Date` trims the time to midnight 00:00:00 since a JDBC date only stores a date (no time). If you try to compare a `java.sql.Date` with the time trimmed to a `java.util.Date` where the time has not been trimmed, `equals()` will return false. It is unfortunate that an Oracle date includes time in its fractional part. For Oracle, a `java.sql.Timestamp` is more appropriate. – jbruni May 02 '17 at 18:21
0

According to Java doc, it is suggested to use appropriate Date type as per underlying database. However, with Java 8, a rich set of classes under java.time package have been provided and it must be used if application is written with Java 8.

The class javaxjava.sql.Date extends java.util.Date with minor changes for miliseconds container so that it can support the Database DATE type effectively. This, we can save the @Temporal annotation typing from entity class.

However, java.util.Date could be used for better scalability in entire application so that it can be easily used to store time along with date.

Sandeep Kumar
  • 596
  • 5
  • 7