Reputation: 11005
The java.util.Date
class has a method called toInstant()
that converts the Date
instance to a java.time.Instant
.
The java.sql.Date
class extends the java.util.Date
class, but when I attempt to call toInstant()
on a java.sql.Date
, I receive an UnsupportedOperationException
.
Why is toInstant()
an unsupported operation on java.sql.Date
?
And what is the "correct" way to convert a java.sql.Date
to a java.time.Instant
?
Upvotes: 82
Views: 61861
Reputation: 9665
java.sql.Date supports only Date components (date, month, year). It does NOT support time components (hour, minute, second, millisecond). toInstant requires both Date and Time components, so toInstant on java.sql.Date instance throws UnsupportedOperationException exception.
toInstant Java doc
This method always throws an UnsupportedOperationException and should not be used because SQL Date values do not have a time component.
java.util.Date OR java.sql.Timestamp has both Date/Time components, so toInstant() works!
You can do like this:
// Time is 00:00:00.000
new java.util.Date(sqlDate.getTime()).toInstant()
Updated:
Instant.ofEpochMilli(sqlDate.getTime());
// and
new java.util.Date(sqlDate.getTime()).toInstant();
will return the same result because the toInstant() method calls Instant.ofEpochMilli(getTime()) internally.
public Instant toInstant() {
return Instant.ofEpochMilli(getTime());
}
Upvotes: 38
Reputation: 91
The implementation of java.sql.Date (imho) is really not perfect.
In order to convert java.util.Date to LocalDate the web is full of code blocks like this:
dateToConvert.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
As developer I'd like to have one method working properly - toLocalDate (internally using deprectated methods) only exists for java.sql.Date so cannot be used on java.util.Date, and toInstant fails on java.sql.Date.
I changed my "Date to LocalDate" conversion code to this:
public static LocalDate asLocalDate(java.util.Date date) {
return date == null ? null : LocalDate.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
}
This works independent of the date type passed in. Basically it means "dont call java.util.Date.toInstant() as you might get a java.sql.Date, which then breaks your code.
I really don't understand why they implement such traps on purpose within the JDK.
Upvotes: 3
Reputation: 22005
According to the JavaDoc
Since sql.Date
does not have a time component, there is no possibility to convert it to time.Instant
This method always throws an UnsupportedOperationException and should not be used because SQL Date values do not have a time component.
Upvotes: 42
Reputation: 19918
If you don't have time in your Date - convert it to millis:
Instant.ofEpochMilli(date.getTime())
.atZone(ZoneId.systemDefault())
.toLocalDate();
Upvotes: 6
Reputation: 328913
The correct mapping between java.sql.Date
and java.time
is LocalDate
:
LocalDate date = sqlDate.toLocalDate();
If you really must, you can then derive an Instant
, although the extra information (time) will be arbitrary. For example:
Instant i = date.atStartOfDay(ZoneOffset.UTC).toInstant();
Upvotes: 64
Reputation: 44071
The answers given so far until now concentrate on the detail that java.sql.Date
has no time information. That is correct but not the real or sufficient reason why this type cannot offer a direct conversion to Instant
. Unfortunatly the documentation of Java-8 does make the same mistake to let users think the problem is just because of missing time information.
Conceptually, the type java.sql.Date
represents a local type. It models a calendar date which can be different in any region of our globe. But an Instant
is the same on our globe around. So users need a timezone or a timezone offset to do the conversion.
Tragically, the type java.sql.Date
inherits from java.util.Date
which is a global type (instant-like). However, this inheritance really denotes implementation inheritance, and not type inheritance. One more reason to consider the design of these old JDBC-classes to be broken. Therefore it is indeed possible to use the hack to wrap the java.sql.Date
via its method getTime()
inside an instance of java.util.Date
which finally allows direct conversion to an instant. But: This conversion implicitly uses the default timezone of the system.
So how to correctly convert in a pedantic way? Let's consider the documentation of Java-8 again which here points into the right direction:
java.sql.Date sqlDate = ...;
LocalDate calendarDate = sqlDate.toLocalDate();
ZonedDateTime zdt = calendarDate.atStartOfDay(ZoneId.of("Europe/Paris"));
Instant instant = zdt.toInstant();
Upvotes: 20