Truncating java.util.Date to LocalDate *without* toInstant() because java.sql.Date gives UnsupportedOperationException

Adam picture Adam · Oct 17, 2017 · Viewed 8.3k times · Source

If I use java.util.Date's toInstant() on a variable which happens to be a java.sql.Date, I get an UnsupportedOperationException.

try {
    java.util.Date input = new java.sql.Date(System.currentTimeMillis());
    LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
} catch (UnsupportedOperationException e) {
    // grrr!
}

The java.util.Date that I'm concerned with comes from a DATE field in a mysql DB via a legacy API, and is actually a java.sql.Date.

Now the following related questions are all very interesting:

UnsupportedOperationException - Why can't you call toInstant() on a java.sql.Date?

Convert java.util.Date to java.time.LocalDate

LocalDate to java.util.Date and vice versa simplest conversion?

but they don't provide any elegant way of truncating a java.util.Date to get rid of the time component and get a Java 8 LocalDate.

I admit there is an issue that the same instant in time in one timezone might be a different date from the same instant in another timezone.

I suspect the solution will involve java.util.Calendar but rather than craft my own solution I'd rather establish what others have done first.

I'd prefer to find something shorter than this:

from Resetting the time part of a timestamp in Java :

Date date = new Date();                      // timestamp now
Calendar cal = Calendar.getInstance();       // get calendar instance
cal.setTime(date);                           // set cal to date
cal.set(Calendar.HOUR_OF_DAY, 0);            // set hour to midnight
cal.set(Calendar.MINUTE, 0);                 // set minute in hour
cal.set(Calendar.SECOND, 0);                 // set second in minute
cal.set(Calendar.MILLISECOND, 0);            // set millis in second
Date zeroedDate = cal.getTime();             // actually computes the new Date

Answer

Adam picture Adam · Oct 20, 2017

Often the simplest solutions are the hardest to find:

public LocalDate convertDateObject(java.util.Date suspectDate) {

    try {
        // Don't do this if there is the smallest chance 
        // it could be a java.sql.Date!
        return suspectDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

    } catch (UnsupportedOperationException e) {
        // BOOM!!
    }

    // do this first:
    java.util.Date safeDate = new Date(suspectDate.getTime());

    return safeDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

}