Legacy Date API

Basic Java
≈ 2 min read

Why you still need to know the old classes

Modern date and time code is usually built on java.time, but older projects and some APIs still use java.util.Date, Calendar, and SimpleDateFormat. It is important to be able to read and convert them even if new code should not be built around them.

Date

Despite its name, Date in modern use effectively represents an instant with millisecond precision relative to the epoch. The class still contains many deprecated methods for manipulating date components, so it should not be treated as a convenient calendar model.

Date legacy = new Date();
Instant instant = legacy.toInstant();

The conversion back is just as direct:

Date legacy = Date.from(instant);

This bridge allows you to keep the old type only at the API boundary and use Instant and other java.time classes inside the application.

Calendar

Calendar appeared as a more capable calendar model, but it has mutable state, a complicated API, and historically awkward details. For example, months in some old calls are numbered from zero.

Calendar calendar = Calendar.getInstance();
calendar.set(2026, Calendar.SEPTEMBER, 18);

In new code, similar tasks are usually expressed more clearly with LocalDate, LocalDateTime, or ZonedDateTime, depending on the meaning of the value.

SimpleDateFormat

The legacy API often used SimpleDateFormat for formatting. An important problem with it is mutability and the lack of thread safety. The modern DateTimeFormatter is immutable and thread-safe, so it is usually a safer choice.

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
String text = LocalDate.of(2026, 9, 18).format(formatter);

How to work with legacy APIs in a project

A practical approach is to convert the old type as close to the integration boundary as possible. For example, if a library returns Date, convert it to Instant immediately and use the modern model afterward. The same idea works in reverse if an older API requires a Date.

Do not change the meaning during migration. First determine what the old value actually represented: an instant, a local date, a local date-time, or a value in a particular zone. Then choose the matching java.time class.