Date Class in Java – java.util.Date and the Modern java.time API
java.util.Date represents a specific instant in time, stored as the number of milliseconds since 1 January 1970, 00:00:00 UTC (the Unix epoch). It has been part of Java since version 1.0 and still appears in older code and APIs such as JDBC and many libraries.
Most of Date's methods are deprecated, and since Java 8 the java.time API (LocalDate, LocalDateTime, ZonedDateTime, Instant) is the recommended replacement. This tutorial shows how to use Date, explains its problems, and shows the modern equivalents and conversions.
1. Creating, Formatting and Comparing Dates
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
public class DateBasics {
public static void main(String[] args) throws Exception {
Date now = new Date(); // current instant
System.out.println("new Date() is close to currentTimeMillis: "
+ (Math.abs(now.getTime() - System.currentTimeMillis()) < 1000));
Date launch = new Date(1_791_000_000_000L); // milliseconds since 1970-01-01T00:00Z
System.out.println("getTime() : " + launch.getTime());
SimpleDateFormat fmt = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss zzz", Locale.ENGLISH);
fmt.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata")); // Date has no time zone itself
System.out.println("formatted (IST): " + fmt.format(launch));
fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println("formatted (UTC): " + fmt.format(launch));
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd");
parser.setTimeZone(TimeZone.getTimeZone("UTC"));
Date d = parser.parse("2026-10-11");
System.out.println("parsed millis : " + d.getTime());
System.out.println("before/after : " + d.before(launch) + " / " + d.after(launch));
System.out.println("compareTo : " + d.compareTo(launch));
}
}
new Date() is close to currentTimeMillis: true getTime() : 1791000000000 formatted (IST): 03-10-2026 09:30:00 IST formatted (UTC): 03-10-2026 04:00:00 UTC parsed millis : 1791676800000 before/after : false / true compareTo : 1
new Date()captures the current instant;new Date(millis)a specific one.- A
Datehas no time zone.toString()uses the JVM's default zone, which is why the same instant shows different clock times in IST and UTC. SimpleDateFormatformats and parses dates with patterns, but it is not thread-safe.- Compare with
before,after,compareToorgetTime().
2. Useful Methods
| Method | Description |
|---|---|
Date(), Date(long millis) | Non-deprecated constructors |
long getTime() / setTime(long) | Milliseconds since the epoch |
before(Date), after(Date), compareTo(Date) | Comparison |
toInstant(), Date.from(Instant) | Conversion to and from java.time (Java 8) |
getYear(), getMonth(), getDate(), getHours()… | Deprecated; use Calendar or java.time |
3. Problems with java.util.Date
import java.util.Date;
public class DateProblems {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
// 1. Confusing, deprecated constructor: year is offset from 1900, month is 0-based
Date d = new Date(126, 9, 11); // means 2026-10-11!
System.out.println("getYear() = " + d.getYear() + ", getMonth() = " + d.getMonth());
// 2. Mutable: anyone holding the reference can change it
Date deadline = new Date(0);
Date alias = deadline;
alias.setTime(86_400_000L); // one day later
System.out.println("deadline changed through alias: " + (deadline.getTime() == 86_400_000L));
}
}
getYear() = 126, getMonth() = 9 deadline changed through alias: true
- Confusing values: years are offset from 1900 and months start at 0.
- Mutable: shared
Dateobjects can be changed unexpectedly, so classes must make defensive copies. - Misleading name: it is an instant, not a calendar date, and has no time zone.
- Not thread-safe helpers:
SimpleDateFormatbreaks when shared between threads. - Poor date arithmetic: adding days or months needs
Calendar.
4. The Modern Alternative: java.time
The java.time package (JSR 310, Java 8) fixes all these issues. Its classes are immutable, thread-safe and clearly named:
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.Locale;
public class ModernDateTime {
public static void main(String[] args) {
LocalDate date = LocalDate.of(2026, 10, 11); // date only, immutable
LocalDate due = date.plusDays(30);
System.out.println("date + 30 days : " + due + " (" + due.getDayOfWeek() + ")");
System.out.println("days between : " + ChronoUnit.DAYS.between(date, due));
LocalDateTime meeting = LocalDateTime.of(2026, 10, 11, 15, 30);
ZonedDateTime ist = meeting.atZone(ZoneId.of("Asia/Kolkata"));
System.out.println("IST : " + ist);
System.out.println("New York : " + ist.withZoneSameInstant(ZoneId.of("America/New_York")));
DateTimeFormatter f = DateTimeFormatter.ofPattern("dd MMM yyyy, hh:mm a", Locale.ENGLISH);
System.out.println("formatted : " + meeting.format(f));
System.out.println("parsed : " + LocalDate.parse("2026-12-25").getDayOfWeek());
// Converting between legacy Date and java.time
Instant instant = ist.toInstant();
Date legacy = Date.from(instant);
System.out.println("round trip : " + legacy.toInstant().equals(instant));
}
}
date + 30 days : 2026-11-10 (TUESDAY) days between : 30 IST : 2026-10-11T15:30+05:30[Asia/Kolkata] New York : 2026-10-11T06:00-04:00[America/New_York] formatted : 11 Oct 2026, 03:30 PM parsed : FRIDAY round trip : true
| Need | Legacy | java.time |
|---|---|---|
| Instant on the timeline | Date | Instant |
| Date without time (birthday) | Date / Calendar | LocalDate |
| Date and time without zone | Date | LocalDateTime |
| Date and time in a zone | Calendar + TimeZone | ZonedDateTime |
| Formatting | SimpleDateFormat (not thread-safe) | DateTimeFormatter (thread-safe) |
| Amount of time | Manual millisecond arithmetic | Duration, Period |
When an API still requires Date, convert at the boundary with Date.from(instant) and date.toInstant(). For JDBC, java.sql.Date and Timestamp can be replaced by LocalDate and LocalDateTime with modern drivers.
5. Summary
java.util.Datestores an instant as milliseconds since the epoch and has no time zone.- Format and parse it with
SimpleDateFormat, which is not thread-safe. - It is mutable and most methods are deprecated.
- Use
java.time(LocalDate,ZonedDateTime,Instant) for new code and convert at API boundaries.
Related Tutorials
Frequently Asked Questions (FAQ)
Q1. What is the Date class in Java?
java.util.Date represents an instant in time as milliseconds since 1 January 1970 UTC.
Q2. Is java.util.Date deprecated?
The class itself is not, but most of its constructors and getters are. New code should use java.time.
Q3. How do I get the current date?
Legacy: new Date(). Modern: LocalDate.now(), LocalDateTime.now() or Instant.now().
Q4. How do I format a Date?
Use new SimpleDateFormat("dd-MM-yyyy").format(date), or convert to java.time and use DateTimeFormatter.
Q5. How do I convert a String to a Date?
new SimpleDateFormat("yyyy-MM-dd").parse(text), which throws ParseException for invalid input. Modern: LocalDate.parse(text).
Q6. Does Date store a time zone?
No. It stores only an instant. toString() displays it in the JVM's default time zone.
Q7. Is SimpleDateFormat thread-safe?
No. Create one per thread or per use, or switch to the immutable DateTimeFormatter.
Q8. Why does getMonth() return 9 for October?
Months in Date and Calendar are zero-based (January is 0), a frequent source of bugs. java.time uses 1–12.
Q9. Is Date mutable?
Yes. setTime changes it, so classes should defensively copy Date fields.
Q10. How do I convert Date to LocalDate?
date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate().
Q11. How do I convert LocalDateTime to Date?
Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()).
Q12. How do I compare two dates?
Use before, after, equals or compareTo. In java.time, use isBefore, isAfter and isEqual.
Q13. What is the difference between java.util.Date and java.sql.Date?
java.sql.Date extends java.util.Date for SQL DATE columns and represents only the date part. Modern JDBC uses LocalDate instead.
Q14. How do I add days to a Date?
Use Calendar.add(Calendar.DAY_OF_MONTH, n), or convert to LocalDate and call plusDays(n).
Q15. What is the epoch?
The reference instant 1970-01-01T00:00:00Z from which Date, Instant and System.currentTimeMillis() count time.
Q16. Why was java.time introduced?
To replace the confusing, mutable and thread-unsafe Date, Calendar and SimpleDateFormat with a clear, immutable API.
Q17. How do I calculate days between two dates?
ChronoUnit.DAYS.between(start, end) with LocalDate values.
Q18. Which Date constructors are not deprecated?
Date() and Date(long millis).
Q19. How do I get milliseconds from a Date?
Call getTime().
Q20. Can Date represent dates before 1970?
Yes, using negative millisecond values.