BackendBench
Coffee

Unravel โ€บ Java concepts โ€บ Time and dates

Java language

Legacy Date, Calendar and SimpleDateFormat

java.util.Date, Calendar and SimpleDateFormat are the pre-Java-8 date classes.

๐Ÿ”Ž Unravel this example

At a glance

Mental model

A pocket calculator from the 1990s with buttons in odd places: it works if you know the quirks, but a modern one is a better choice.

What happens, in order
  1. 1new Date() is a moment (milliseconds since 1970), not a dateIts toString prints it in the default time zone, which makes it look like a zoned value.
  2. 2Calendar months start at 0 (January is 0), Calendar.getInstance() uses the default zone, and cal.add(Calendar.DAY_OF_MONTH, 1) mutates the object
  3. 3SimpleDateFormat("yyyy-MM-dd") formats and parses, but it is not thread safe: one shared static instance corrupts results under loadIt also accepts invalid dates (February 30) by default unless you call setLenient(false).
  4. 4Bridges: date.toInstant(), Date.from(instant), LocalDate.ofInstant(...), Timestamp.valueOf(localDateTime) for JDBC
Remember
Watch out
Sharing one SimpleDateFormat between threadsUsing month numbers 1-12 with CalendarAssuming Date knows a time zoneUsing YYYY in the pattern
Example (highlighted lines explain it)
import java.text.SimpleDateFormat;import java.time.*;import java.util.*;public class Old {  static String format(Date d) {    SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");    f.setLenient(false);    return f.format(d);  }  static Date tomorrow() { Calendar c = Calendar.getInstance(); c.add(Calendar.DAY_OF_MONTH, 1); return c.getTime(); }  static LocalDate modern(Date d) { return d.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); }}
  1. line 1 uses SimpleDateFormat, the core of Legacy Date, Calendar and SimpleDateFormat
  2. line 5 uses Date, the core of Legacy Date, Calendar and SimpleDateFormat
  3. line 6 uses SimpleDateFormat, the core of Legacy Date, Calendar and SimpleDateFormat
  4. line 10 uses Date, the core of Legacy Date, Calendar and SimpleDateFormat
  5. line 11 uses Date, the core of Legacy Date, Calendar and SimpleDateFormat
Read the full explanation (about 1 min)

java.util.Date, Calendar and SimpleDateFormat are the pre-Java-8 date classes. They are mutable, not thread safe and full of surprises; java.time replaces them, but old code and some libraries still use them.

A mental model

A pocket calculator from the 1990s with buttons in odd places: it works if you know the quirks, but a modern one is a better choice. The conversion cable between old and new is toInstant() and Date.from().

How it works

new Date() is a moment (milliseconds since 1970), not a date. Its toString prints it in the default time zone, which makes it look like a zoned value.

Calendar months start at 0 (January is 0), Calendar.getInstance() uses the default zone, and cal.add(Calendar.DAY_OF_MONTH, 1) mutates the object.

SimpleDateFormat("yyyy-MM-dd") formats and parses, but it is not thread safe: one shared static instance corrupts results under load. It also accepts invalid dates (February 30) by default unless you call setLenient(false).

Bridges: date.toInstant(), Date.from(instant), LocalDate.ofInstant(...), Timestamp.valueOf(localDateTime) for JDBC.

Going deeper

The year 2000 and January numbering bugs of these classes caused so many errors that Java 8 added java.time. In new code avoid the legacy classes except at the boundary of libraries (JDBC Timestamp, JPA 1 @Temporal).

A Date field exposed through a getter lets callers modify your object: copy it (new Date(d.getTime())) or, better, store an Instant.

java.sql.Date stores only a date but extends java.util.Date; Timestamp has nanoseconds and its equals is not symmetric with Date.

Under the hood

Date is a single long of epoch milliseconds. Calendar is a bag of fields (year, month, day...) that are lazily recomputed from the millisecond value, which is where much of its complexity and mutability comes from.

Common mistakes

  • Sharing one SimpleDateFormat between threads.
  • Using month numbers 1-12 with Calendar.
  • Assuming Date knows a time zone.
  • Using YYYY in the pattern.

How Unravel recognises it

When you paste code, Unravel looks for these signs of legacy date, calendar and simpledateformat. Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Frequently asked questions

What is Legacy Date, Calendar and SimpleDateFormat?

java.util.Date, Calendar and SimpleDateFormat are the pre-Java-8 date classes. They are mutable, not thread safe and full of surprises; java.time replaces them, but old code and some libraries still use them.

What are the most common mistakes with Legacy Date, Calendar and SimpleDateFormat?

Sharing one SimpleDateFormat between threads. Using month numbers 1-12 with Calendar. Assuming Date knows a time zone. Using YYYY in the pattern.

How can I see Legacy Date, Calendar and SimpleDateFormat in real code?

Paste your own Java into Unravel and it will point out where legacy date, calendar and simpledateformat appears, explain how it works at the detail level you choose, and draw the flow of the code around it.