Java Date Class

In the last lesson you learned about Java regular expressions. Almost every program needs to hold a point in time, like an order date or a payment time. Java’s oldest answer is a class called Date, in java.util, and it still shows up in old code.

🤔 What is a Date?

A Date holds one exact moment in time, down to the millisecond.

  • It does not store year, month, and day as separate fields.
  • It stores one big number: milliseconds since a fixed starting point.
  • That starting point is midnight on the 1st of January 1970, called the epoch.
  • Year, month, and the rest are worked out from that number when you ask.

🆕 Creating a Date for right now

The most common thing is asking for the current moment. new Date() with no arguments captures the instant your code runs.

This example makes a Date and prints it.

import java.util.Date;
public class Main {
public static void main(String[] args) {
Date now = new Date(); // captures this exact moment
System.out.println(now);
}
}

Here is what happens:

  • new Date() reads the computer’s clock and stores that instant.
  • So now holds the moment the line ran.
  • Your output will be whatever date and time it is when you run it.

Output

Sat Jun 13 10:25:41 IST 2026

That printed shape is the default text: day name, month, day number, time, time zone, year. We will control that format later.

⏱️ getTime and the epoch

getTime reads the millisecond number directly and hands back a long.

This example prints the raw millisecond count.

import java.util.Date;
public class Main {
public static void main(String[] args) {
Date now = new Date();
long millis = now.getTime(); // milliseconds since 1 Jan 1970
System.out.println("Milliseconds since the epoch: " + millis);
}
}

Here is what that number means:

  • now.getTime() returns milliseconds passed since the epoch.
  • It is huge because many years have gone by.
  • This single number is the heart of the whole class.

Output

Milliseconds since the epoch: 1781678741000

Why the epoch is useful:

  • A plain number is easy to store, compare, and do maths with.
  • Comparing two moments is just comparing their millisecond counts.
  • The larger number is the later moment.

The epoch is a shared zero

Many systems, not just Java, count time from the 1st of January 1970. So this millisecond number travels well between languages and databases. It is often called a “timestamp”.

🧮 Creating a Date from milliseconds

Because a Date is just a millisecond count, you can build one by passing a long of milliseconds to the constructor.

This example makes the epoch itself, and a moment one day later.

import java.util.Date;
public class Main {
public static void main(String[] args) {
Date epoch = new Date(0); // 0 milliseconds = the epoch start
long oneDay = 24L * 60 * 60 * 1000; // hours, minutes, seconds, millis
Date dayLater = new Date(oneDay); // one full day after the epoch
System.out.println("Epoch: " + epoch);
System.out.println("Day later: " + dayLater);
}
}

What the numbers do:

  • 0 gives the exact start of the epoch.
  • oneDay (the milliseconds in 24 hours) gives the moment one day later.
  • So you move through time just by changing the number.
  • The printed time zone may differ on your machine, so your output can vary.

Output

Epoch: Thu Jan 01 05:30:00 IST 1970
Day later: Fri Jan 02 05:30:00 IST 1970

The epoch shows 05:30 here, not 00:00, because the printout uses the local time zone (this machine is five and a half hours ahead). The stored number is still zero. Only the display shifts.

⚖️ Comparing two Dates

Because each Date is built on a number, three clear methods compare them: before, after, and equals. Each returns true or false.

This example compares two moments.

import java.util.Date;
public class Main {
public static void main(String[] args) {
Date earlier = new Date(1000); // 1 second after the epoch
Date later = new Date(5000); // 5 seconds after the epoch
System.out.println(earlier.before(later)); // is earlier before later?
System.out.println(earlier.after(later)); // is earlier after later?
System.out.println(earlier.equals(later)); // are they the same moment?
}
}

What each method asks:

  • before asks if this moment comes first.
  • after asks if it comes last.
  • equals asks if both hold the very same millisecond.
  • Here the first line is true, the other two false.

Output

true
false
false

You could also call getTime on both and compare the numbers yourself. Both ways work, but before and after read more clearly.

🚫 The deprecated methods

Date used to have methods like getYear, getMonth, getDate, getHours, and setYear. Almost all are deprecated: still here, but you should not use them, and they may be removed later. The compiler warns you when you call one.

This example shows the kind of code you should avoid.

import java.util.Date;
public class Main {
public static void main(String[] args) {
Date now = new Date();
int year = now.getYear(); // ❌ deprecated, and gives a confusing value
int month = now.getMonth(); // ❌ deprecated, and is 0-based
System.out.println("getYear(): " + year);
System.out.println("getMonth(): " + month);
}
}

Why they were deprecated:

  • getYear returns the year minus 1900, so 2026 comes back as 126.
  • getMonth counts from zero, so January is 0 and June is 5.
  • These quirks caused endless bugs, so Java pushed them aside.

Output

getYear(): 126
getMonth(): 5

Do not lean on these methods

The deprecated getters and setters are confusing and unsafe. The right way to read parts of a date is with the newer java.time classes, which we will reach in the next lessons. Treat the deprecated methods as “read-only history” you should recognise but not write.

✏️ Date is mutable

A Date can be changed after you create it. We say it is mutable: its value can be altered in place. The method setTime points the same object at a different moment.

This example changes a Date after creating it.

import java.util.Date;
public class Main {
public static void main(String[] args) {
Date d = new Date(0); // starts at the epoch
System.out.println("Before: " + d.getTime());
d.setTime(60000); // now points at 60 seconds after the epoch
System.out.println("After: " + d.getTime());
}
}

Why this is a trap:

  • setTime(60000) overwrites the moment d holds, so the same object means a different time.
  • Hand the same Date to two parts of your program, and one changes it.
  • The other part is changed too, without knowing.

Output

Before: 0
After: 60000

This mutability is also why Date is not thread-safe. If one thread calls setTime while another reads the same Date, you can get a wrong or half-changed value. So sharing one Date across threads is risky.

🎨 Formatting a Date with SimpleDateFormat

The default Date printout is fixed and ugly. The helper class SimpleDateFormat controls the look: you give it a pattern of letters, and it turns a Date into neat text.

This example formats one moment in two different ways.

import java.util.Date;
import java.text.SimpleDateFormat;
public class Main {
public static void main(String[] args) {
Date now = new Date();
SimpleDateFormat dayFirst = new SimpleDateFormat("dd-MM-yyyy");
SimpleDateFormat withTime = new SimpleDateFormat("dd MMM yyyy, HH:mm:ss");
System.out.println(dayFirst.format(now));
System.out.println(withTime.format(now));
}
}

How it works:

  • You build a SimpleDateFormat and pass the pattern as a String.
  • The pattern letters stand for parts of the date.
  • format takes a Date and returns the text you asked for.
  • Your exact output depends on the current moment.

Output

13-06-2026
13 Jun 2026, 10:25:41

The common pattern letters:

  • dd is the day of the month, two digits.
  • MM is the month as a number, while MMM is the short month name like Jun.
  • yyyy is the four-digit year.
  • HH is the hour on a 24-hour clock, mm is minutes, and ss is seconds.

Capital M and small m are different

In a pattern, capital M means month and small m means minutes. Mixing them up is a classic bug. If your minutes look like a month, check the case of that letter.

🔄 Reading a Date back from text

SimpleDateFormat works the other way too. Taking a String that follows the pattern and turning it into a Date is called parsing. The method is parse, and it can throw a checked error, so we handle it.

This example reads a date written as text.

import java.util.Date;
import java.text.SimpleDateFormat;
public class Main {
public static void main(String[] args) throws Exception {
SimpleDateFormat fmt = new SimpleDateFormat("dd-MM-yyyy");
Date parsed = fmt.parse("25-12-2026"); // text into a Date
System.out.println(parsed);
}
}

How parsing works:

  • The pattern tells parse how to read the String.
  • So "25-12-2026" is understood as the 25th of December 2026.
  • If the text does not match the pattern, parse throws an error.
  • That is why the method declares throws Exception.

Output

Fri Dec 25 00:00:00 IST 2026

🆕 Why modern Java replaced Date

By now you can feel the problems:

  • The handy getters are deprecated and confusing.
  • The object can be changed under your feet.
  • Date is not thread-safe, and neither is SimpleDateFormat.

So Java 8 brought a new package, java.time, that fixes all of this:

  • LocalDate for a date with no time.
  • LocalTime for a time with no date.
  • LocalDateTime for both together.

This small taste shows how much cleaner the new way reads.

import java.time.LocalDate;
public class Main {
public static void main(String[] args) {
LocalDate today = LocalDate.now();
System.out.println("Today: " + today);
System.out.println("Year: " + today.getYear()); // the real year
System.out.println("Month: " + today.getMonthValue()); // 1 to 12
}
}

Look at the difference:

  • getYear returns the true year, not the year minus 1900.
  • The month value runs from 1 to 12, the way a human counts.
  • These objects cannot be changed once made, so they are safe to share.
  • The default printout is clean and standard.

Output

Today: 2026-06-13
Year: 2026
Month: 6

We will dig into LocalDate, LocalTime, and friends in the coming lessons. The rule: know Date because you will meet it, but reach for java.time in new code.

⚠️ Common Mistakes

A few Date mistakes catch nearly everyone.

Using the deprecated getters. Methods like getYear and getMonth give confusing values and are marked for removal.

Date d = new Date();
int y = d.getYear(); // ❌ returns the year minus 1900, like 126 for 2026
// prefer java.time, where LocalDate.now().getYear() gives 2026 ✅

Forgetting getMonth is 0-based. On the old Date, January is 0, not 1, so the number is always one less than you expect.

Date d = new Date();
System.out.println(d.getMonth()); // ❌ June prints as 5, not 6
System.out.println(d.getMonth() + 1); // ✅ add 1 if you must read it this way

Sharing one mutable Date. Because setTime changes the object in place, two parts of your code can end up changing the same moment by accident.

Date shared = new Date(0);
Date alsoShared = shared; // ❌ both names point to the SAME object
alsoShared.setTime(99999); // this also changes "shared"
System.out.println(shared.getTime()); // 99999, surprise
// ✅ make a fresh copy when you need an independent moment
Date safeCopy = new Date(shared.getTime());

✅ Best Practices

Good habits when time shows up in your code:

  • Prefer java.time for new code. Use LocalDate, LocalTime, and LocalDateTime instead of Date. They are clearer, safer, and not deprecated.
  • Never call the deprecated getters or setters. If you must read parts of an old Date, convert it to a modern type first.
  • Do not share a SimpleDateFormat across threads. It is not thread-safe. Create a new one where you need it, or use the thread-safe DateTimeFormatter from java.time.
  • Treat a Date as a fixed moment, not a calendar. It is just a millisecond count. For year, month, and day work, use the newer classes.
  • Copy a Date before passing it around. Because it is mutable, hand out new Date(other.getTime()) so callers cannot change your value.

🧩 What You’ve Learned

Nicely done. The old Date class is full of sharp edges, but now you can use it safely and you know why the modern classes exist. The key ideas:

  • ✅ A Date holds one moment in time, stored as milliseconds since the epoch, the 1st of January 1970.
  • new Date() captures right now, and new Date(millis) builds a moment from a number.
  • getTime returns that millisecond count, which makes comparing moments easy.
  • ✅ Most getters like getYear and getMonth are deprecated, and getMonth is 0-based.
  • ✅ A Date is mutable and not thread-safe, so copy it before sharing.
  • ✅ Use SimpleDateFormat with a pattern to format and parse, but prefer the newer java.time classes in new code.

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What does getTime return on a Date?

    Why: getTime returns the count of milliseconds since the epoch, which is midnight on the 1st of January 1970.

  2. 2

    Why are methods like getYear and getMonth on Date a problem?

    Why: They are deprecated. getYear returns the year minus 1900, and getMonth is 0-based, both of which cause bugs.

  3. 3

    What number does getMonth return for June?

    Why: getMonth is 0-based, so January is 0 and June is 5, one less than the human month number.

  4. 4

    Which class is recommended for new date and time code in Java?

    Why: Modern Java uses the java.time classes such as LocalDate, which are safe, immutable, and not deprecated.

🚀 What’s Next?

You now understand the old Date class, its millisecond core, and why it has so many warnings on it. Before we jump to the modern classes, there is one more old helper worth knowing, because it appears in plenty of older code too. It is the class that handled date arithmetic and fields back in the day. Let’s look at it next.

Java Calendar Class

Share & Connect