Converting Between Legacy and New Date Classes

Naveen AnamaneniUpdated · 4 min read
mediumJava 25javadate-timelegacy

New code should use java.time, but older libraries and databases still use java.util.Date, Calendar, java.sql.Date, java.sql.Timestamp and TimeZone. To connect the two worlds, Java 8 added bridge methods to the legacy classes, such as date.toInstant() and Date.from(instant). In this lesson, you will learn how to convert in both directions, so you can keep your own code in java.time and convert only at the edges.

Conversion Methods

FromToMethod
java.util.DateInstantdate.toInstant()
Instantjava.util.DateDate.from(instant)
CalendarInstantcalendar.toInstant()
GregorianCalendarZonedDateTimegc.toZonedDateTime()
ZonedDateTimeGregorianCalendarGregorianCalendar.from(zdt)
TimeZoneZoneIdtimeZone.toZoneId()
ZoneIdTimeZoneTimeZone.getTimeZone(zoneId)
java.sql.DateLocalDatesqlDate.toLocalDate()
LocalDatejava.sql.Datejava.sql.Date.valueOf(localDate)
java.sql.TimestampLocalDateTimets.toLocalDateTime()
LocalDateTimejava.sql.TimestampTimestamp.valueOf(ldt)

A java.util.Date is an instant, so converting it to a LocalDate needs a time zone: date.toInstant().atZone(zone).toLocalDate().

Date and Instant are both moments; reaching a local date needs a time zone

Example: Converting Both Ways

Let's see an example that converts a legacy Date and back.

Bridge.java▶ Try it
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
 
public class Bridge {
    public static void main(String[] args) {
        Date legacy = new Date(1_794_490_200_000L);
 
        Instant instant = legacy.toInstant();
        System.out.println("Instant:   " + instant);
 
        ZoneId india = ZoneId.of("Asia/Kolkata");
        LocalDate local = instant.atZone(india).toLocalDate();
        System.out.println("LocalDate: " + local + " in " + india);
 
        Date back = Date.from(instant);
        System.out.println("Round trip equal? " + back.equals(legacy));
 
        GregorianCalendar cal = GregorianCalendar.from(instant.atZone(india));
        System.out.println("Calendar hour: " + cal.get(Calendar.HOUR_OF_DAY));
        ZonedDateTime zdt = cal.toZonedDateTime();
        System.out.println("From calendar: " + zdt);
 
        System.out.println("TimeZone to ZoneId: "
            + TimeZone.getTimeZone("Europe/London").toZoneId());
    }
}
Output
Instant:   2026-11-12T13:30:00Z
LocalDate: 2026-11-12 in Asia/Kolkata
Round trip equal? true
Calendar hour: 19
From calendar: 2026-11-12T19:00+05:30[Asia/Kolkata]
TimeZone to ZoneId: Europe/London

Explanation: The Date is a moment in time, so toInstant() gives the same moment as an Instant. To get a LocalDate, a zone must be chosen. Date.from converts back with no loss. GregorianCalendar.from and toZonedDateTime convert between calendars and zoned date-times.

Example: JDBC Dates

SqlDates.java▶ Try it
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
 
public class SqlDates {
    public static void main(String[] args) {
        java.sql.Date sqlDate =
            java.sql.Date.valueOf(LocalDate.of(2026, 11, 12));
        System.out.println("sql.Date: " + sqlDate);
        System.out.println("Back to LocalDate: " + sqlDate.toLocalDate());
 
        Timestamp ts = Timestamp.valueOf(LocalDateTime.of(2026, 11, 12, 9, 30));
        System.out.println("Timestamp: " + ts);
        System.out.println("Back to LocalDateTime: " + ts.toLocalDateTime());
    }
}
Output
sql.Date: 2026-11-12
Back to LocalDate: 2026-11-12
Timestamp: 2026-11-12 09:30:00.0
Back to LocalDateTime: 2026-11-12T09:30

Explanation: java.sql.Date and Timestamp have direct conversions to and from LocalDate and LocalDateTime. Modern JDBC drivers can also read and write java.time types directly with getObject(column, LocalDate.class) and setObject, covered later in the course.

Common Mistakes

Calling date.toLocalDate(). java.util.Date has no such method; go through toInstant().atZone(zone).

Using the system default zone without thinking. ZoneId.systemDefault() differs between servers; choose the zone deliberately.

Mixing up java.util.Date and java.sql.Date. java.sql.Date represents only a date and has toLocalDate().

Practice

  1. Easy: Convert new Date() into a LocalDateTime in your own time zone and print it.
  2. Harder: Write two helper methods, toLocalDate(Date d, ZoneId z) and toDate(LocalDate ld, ZoneId z), that convert at midnight in the given zone, and test a round trip.

Quick check: Legacy conversion

4 questions
1Which method turns a java.util.Date into an Instant?
2What do you need to turn an Instant into a LocalDate?
3Which method converts LocalDate to java.sql.Date?
4Where should conversions happen?

Key Takeaways

Was this tutorial helpful?

Lesson 335 of 482