1

I have a java.sql.Date object and want to transform it to a java.time.LocalDateTime object.

For comparison, I am able to do a similar transformation using java.util.Date:

java.util.Date utilDate = new java.util.Date(sqlDate.getTime());
System.out.println("date with time: " + utilDate);

This answer doesn't work for me, as my java.sql.Date does not have a getTimestamp method.

For reference, this question addresses the opposite transformation.

Woodchuck
  • 3,869
  • 2
  • 39
  • 70
  • 2
    You can’t. By definition, you only have a date, with no time part. You can call [toLocalDate()](https://docs.oracle.com/en/java/javase/17/docs/api/java.sql/java/sql/Date.html#toLocalDate()), then call that LocalDate’s [atTime](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/LocalDate.html#atTime(java.time.LocalTime)) method to get a LocalDateTime, but you will have to decide for yourself what time of day you want to apply to the date. – VGR Oct 20 '21 at 15:13
  • U can convert to java.sql.Timestamp(long/Instant time) then localDateTime – Rony Nguyen Oct 20 '21 at 15:16
  • @VGR, the time part is apparently in the java.sql.Date object, as it does accurately appear when I transform it using java.sql.Date.getTime() to a java.util.Date. – Woodchuck Oct 20 '21 at 15:16
  • 1
    Just because the compiler lets you do it doesn’t mean it’s safe or reliable. The decision to have java.sql.Date and java.sql.Time extend java.util.Date was an unfortunate design mistake in Java SE. Although they inherit a getTime method, that method is not reliable in the subclasses and should not be called. In fact, java.sql.Date should not be used at all; the superior replacement is `resultSet.getObject(1, LocalDate.class)`. – VGR Oct 20 '21 at 15:36
  • I'm not saying it's safe or reliable - just that I'm able to pull the time part from a java.sql.Date. If I understand correctly, your recommendation is to not use java.sql.Date. I will take that into account going forward and I much appreciate the input, as it does appear to be sound. It's, however, outside of the scope of my current situation. I'm curious whether there's a way to (reliably or not) transform the java.sql.Date that I currently have to a LocalDateTime. – Woodchuck Oct 20 '21 at 15:52
  • What's interesting to me is the answer I link to above appears to have worked for many (it's marked as accepted and has 45 upvotes), but for some reason doesn't work for me, as java.sql.Date does not seem to have a getTimestamp method. – Woodchuck Oct 20 '21 at 15:57
  • 1
    That's because SQL DATE doesn't have a time, only a date. – Dorian Gray Oct 20 '21 at 17:14
  • 1
    @JWoodchuck `java.sql.Date` extends `java.util.Date` in a futile attempt to pretend to be a date without a time-of-day and zone/offset. Yet it *does* have a time-of-day and a zone/offset. This is an excellent example of how *not* to design your classes. These legacy date-time classes were designed by people who did not understand how date-time handling. I urge you to quickly forget all about these legacy classes. Focus on using only the *java.time* classes. [JSR 310](https://www.jcp.org/en/jsr/detail?id=310) was adopted unanimously for good reason. – Basil Bourque Oct 20 '21 at 20:25

2 Answers2

5

You are using terrible date-time classes that were years ago supplanted by the modern java.time classes defined in JSR 310.

  • Do not use java.sql.Date
  • Do not use java.util.Date
  • Do not use java.sql.Timestamp
  • Do not use java.util.Calendar

Use only java.time classes.

For exchanging date-time values with a database, use JDBC 4.2 or later.

The java.sql.Date class pretends to represent a date-only value.

If you are handed a java.sql.Date object, immediately convert it to a java.time.LocalDate. Use the new method toLocalDate added to that old class.

LocalDate localDate  = myJavaSqlDate.toLocalDate() ;

You asked for a java.time.LocalDateTime object. You have the necessary date portion. Now you need to assign the time-of-day portion.

LocalTime localTime = LocalTime.of( 15 , 30 );

Combine.

LocalDateTime localDateTime = LocalDateTime.of( localDate, localTime ) ;

A LocalDateTime is inherently ambiguous. 3:30 PM in Japan is a different moment than 3:30 PM in Morocco .

To determine a moment, a specific point on the timeline, place your LocalDateTime within the context of a time zone. You get a ZonedDateTimeObject.

ZoneId zoneId = ZoneId.of( "Asia/Tokyo" ) ;
ZonedDateTime zonedDateTime = localDateTime.atZone( zoneId ) ;

To view that moment as seen in UTC, with an offset from UTC of zero hours-minutes-seconds, extract an Instant.

Instant instant = zonedDateTime.toInstant() ;
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
1

To answer your question as asked

There’s a wealth of good information in the clever answer by Basil Bourque. Not least the recommendation to avoid the java.sql.Date class completely so you won’t need the conversion.

For this answer I am assuming that you are getting a java.sql.Date from a legacy API that you can’t afford to upgrade to java.time just now. So you do need some conversion, and you have reasons to ask for a LocalDateTime representing the time in the default time zone of the JVM (a fragile practice). There is still a question to consider: do you want only the date part of the Date, or its point in time? Asking because a java.sql.Date was meant for representing a date without time of day, but the API does not enforce this, and a java.sql.Date holds a point in time with millisecond precision. I hope we already told you that this is a confusing class better to be avoided if you can.

To get the start of the day:

    java.sql.Date oldfashionedSqlDate
            = java.sql.Date.valueOf(LocalDate.of(2021, Month.OCTOBER, 26));
    System.out.println("java.sql.Date: " + oldfashionedSqlDate);
    LocalDateTime dateTime = oldfashionedSqlDate.toLocalDate().atStartOfDay();
    System.out.println("LocalDateTime: " + dateTime);

Output (assuming the default time zone of the JVM has not changed in the meantime):

java.sql.Date: 2021-10-26
LocalDateTime: 2021-10-26T00:00

To get the point in time: To pick up the full precision held in the Date object:

    java.sql.Date oldfashionedSqlDate = new java.sql.Date(1_666_000_000_000L);
    System.out.println("java.sql.Date: " + oldfashionedSqlDate);
    long epochMilli = oldfashionedSqlDate.getTime();
    LocalDateTime dateTime = Instant.ofEpochMilli(epochMilli)
            .atZone(ZoneId.systemDefault())
            .toLocalDateTime();
    System.out.println("LocalDateTime: " + dateTime);

Output in my time zone:

java.sql.Date: 2022-10-17
LocalDateTime: 2022-10-17T03:46:40

Since the conversion is time zone dependent, output in other time zones will differ in most cases.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161