Dates are everywhere in real applications: when an order was placed, when a subscription renews, how many days are left until an event, which shift starts at 9:00 in London but 10:00 in Paris. They're also surprisingly hard to get right, with months of different lengths, leap years, daylight saving changes and different date formats around the world.
Java's java.time package, added in Java 8, handles all of this with clear, reliable classes. Every object is immutable, so adding a day gives you a new date rather than changing the old one, and the names say exactly what each class holds: a date, a time, or both, with or without a time zone.
In this lesson you'll create dates and times, format them for people and parse them from text, add and subtract periods, measure the gap between two dates, and see how time zones and instants fit in. If you've used the older Date or Calendar classes, this is their much friendlier replacement.
The main classes
| Class | Holds | Example |
|---|---|---|
LocalDate | A date | 2024-07-05 |
LocalTime | A time of day | 14:30 |
LocalDateTime | Date and time, no zone | 2024-07-05T14:30 |
ZonedDateTime | Date, time and time zone | 2024-07-05T14:30+01:00[Europe/London] |
Instant | An exact moment, in UTC | 2024-07-05T13:30:00Z |
Create them with now() for the current value or of() for a specific one: LocalDate.of(2024, 7, 5). Months are numbered 1 to 12, so July really is 7. Printing a value gives the international ISO format.
Formatting and parsing
A DateTimeFormatter turns dates into text, and text back into dates, using a pattern:
| Pattern | Meaning | Example |
|---|---|---|
dd / d | Day, two digits / as needed | 05 / 5 |
MM / MMM / MMMM | Month number / short / full name | 07 / Jul / July |
yyyy | Year | 2024 |
EEE / EEEE | Weekday short / full | Fri / Friday |
HH:mm | 24-hour time | 14:30 |
h:mm a | 12-hour time | 2:30 PM |
var fmt = DateTimeFormatter.ofPattern("d MMMM yyyy");
String text = date.format(fmt); // "5 July 2024"
LocalDate back = LocalDate.parse("5 July 2024", fmt);Names like July come from your computer's language setting; pass Locale.UK to ofPattern to fix the language. Invalid text throws DateTimeParseException.
Adding time and measuring gaps
Because dates are immutable, methods like plusDays return a new date:
LocalDate due = borrowed.plusWeeks(3);
LocalDate lastMonth = today.minusMonths(1);To measure a gap:
ChronoUnit.DAYS.between(a, b)gives the total number of days (or weeks, months, hours with other units).Period.between(a, b)gives years, months and days separately, which is ideal for ages.Duration.between(t1, t2)measures time-based gaps in hours, minutes and seconds.
Compare dates with isBefore(), isAfter() and isEqual(). Adding a month to 31 January gives 29 February in a leap year: Java picks the last valid day.
Time zones and instants
A LocalDateTime has no zone, so "9:00" could mean any 9:00 on Earth. For events that involve real moments in different places, attach a zone with atZone(ZoneId.of("Europe/London")) to get a ZonedDateTime, and convert with withZoneSameInstant(ZoneId.of("Asia/Tokyo")).
An Instant is a single point on the timeline in UTC, the best choice for timestamps such as "when was this record created?". Store instants, and convert to a zone only when showing a time to a person. Java's zone data includes daylight saving rules, so conversions stay correct all year round.
Java Dates Syntax
java.time essentials
LocalDate d = LocalDate.of(2024, 7, 5);
LocalDate today = LocalDate.now();
var fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy");
d.format(fmt); LocalDate.parse("05/07/2024", fmt);
d.plusDays(10); d.minusMonths(1); d.getDayOfWeek();
ChronoUnit.DAYS.between(a, b); Period.between(a, b);
Duration.between(t1, t2).toMinutes();
d.isBefore(other);
ZonedDateTime z = dt.atZone(ZoneId.of("Europe/London"));Java Dates Summary
- LocalDate, LocalTime and LocalDateTime hold dates and times without a zone; create them with now() or of().
- DateTimeFormatter.ofPattern formats dates for display and parses text into dates.
- plusDays, minusMonths and friends return new objects; dates are immutable.
- ChronoUnit.DAYS.between counts days; Period gives years, months and days; Duration measures hours and minutes.
- Use ZonedDateTime for zones and Instant for exact moments; avoid the old Date class.
Java Dates Examples
Each example shows the code, the output it produces and what each part does.
A library due date
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
var fmt = DateTimeFormatter.ofPattern("EEEE d MMMM yyyy", Locale.UK);
LocalDate borrowed = LocalDate.of(2024, 7, 5);
LocalDate due = borrowed.plusWeeks(3);
System.out.println("Borrowed: " + borrowed.format(fmt));
System.out.println("Due: " + due.format(fmt));
LocalDate returned = LocalDate.of(2024, 7, 30);
long late = ChronoUnit.DAYS.between(due, returned);
if (returned.isAfter(due)) {
System.out.printf("%d days late, fine £%.2f%n", late, late * 0.25);
}
}
}Borrowed: Friday 5 July 2024
Due: Friday 26 July 2024
4 days late, fine £1.00Line-by-line explanation
- Line 8The pattern spells out the weekday and month, in UK English.
- Line 10plusWeeks returns a new date; borrowed is unchanged.
- Line 15Count the days between the due date and the return date.
- Line 16isAfter compares two dates.
Ages and countdowns
import java.time.LocalDate;
import java.time.Month;
import java.time.Period;
import java.time.temporal.ChronoUnit;
public class Main {
public static void main(String[] args) {
LocalDate today = LocalDate.of(2024, 7, 5);
LocalDate born = LocalDate.of(2008, Month.SEPTEMBER, 14);
Period age = Period.between(born, today);
System.out.println("Age: " + age.getYears() + " years, "
+ age.getMonths() + " months, " + age.getDays() + " days");
LocalDate newYear = LocalDate.of(2025, 1, 1);
System.out.println("Days until New Year: " + ChronoUnit.DAYS.between(today, newYear));
System.out.println("New Year falls on a " + newYear.getDayOfWeek());
System.out.println("Leap year? " + today.isLeapYear());
}
}Age: 15 years, 9 months, 21 days
Days until New Year: 180
New Year falls on a WEDNESDAY
Leap year? trueLine-by-line explanation
- Line 8A fixed "today" keeps the example's output the same every time; use LocalDate.now() in real code.
- Line 9Month.SEPTEMBER is an enum, clearer than the number 9.
- Line 11Period splits the gap into years, months and days, exactly how ages are described.
- Line 17getDayOfWeek returns a DayOfWeek enum value.
Shift times across time zones
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
LocalTime start = LocalTime.of(8, 45);
LocalTime end = LocalTime.of(17, 15);
Duration shift = Duration.between(start, end);
System.out.println("Shift: " + shift.toHours() + " h " + shift.toMinutesPart() + " min");
var fmt = DateTimeFormatter.ofPattern("HH:mm");
ZonedDateTime call = LocalDateTime.of(2024, 7, 5, 9, 0)
.atZone(ZoneId.of("Europe/London"));
for (String zone : new String[]{"Europe/Paris", "America/New_York", "Asia/Tokyo"}) {
ZonedDateTime there = call.withZoneSameInstant(ZoneId.of(zone));
System.out.println(zone + ": " + there.format(fmt));
}
}
}Shift: 8 h 30 min
Europe/Paris: 10:00
America/New_York: 04:00
Asia/Tokyo: 17:00Line-by-line explanation
- Line 12Duration measures the time between two times of day.
- Line 13toMinutesPart gives the minutes left over after the whole hours.
- Lines 16 to 17A 9:00 call in London, in July, during British Summer Time.
- Line 19The same instant, shown in another city's local time.
Common Java Dates Mistakes
Ignoring the returned date
LocalDate d = LocalDate.of(2024, 7, 5);
d.plusDays(7); // result thrown away
System.out.println(d); // still 2024-07-05d = d.plusDays(7);mm instead of MM
DateTimeFormatter.ofPattern("dd/mm/yyyy"); // mm means minutesDateTimeFormatter.ofPattern("dd/MM/yyyy"); // MM means monthUsing Period for a total number of days
int days = Period.between(a, b).getDays(); // only the "days" part, e.g. 21long days = ChronoUnit.DAYS.between(a, b); // the full count, e.g. 295Java Dates in Real Projects
java.time in practice
Booking systems calculate nights with ChronoUnit.DAYS.between, payroll software measures shifts with Duration, and global apps store every timestamp as an Instant and show it in each user's zone. Databases, JSON libraries and frameworks like Spring map LocalDate and Instant directly to and from their own date types, so java.time values flow through a whole application unchanged.
Java Dates FAQ
How do I get the current date in Java?
LocalDate.now() gives today's date in the computer's default time zone, and LocalDateTime.now() adds the time. Pass a zone when it matters, as in LocalDate.now(ZoneId.of("Asia/Tokyo")), because the date differs around the world at the same moment.
How do I convert a String to a LocalDate?
Text in ISO format, such as "2026-10-11", parses directly with LocalDate.parse(text). For other layouts, build a formatter, DateTimeFormatter.ofPattern("dd/MM/yyyy"), and call LocalDate.parse(text, formatter). Catch DateTimeParseException for text typed by users.
How do I calculate someone's age in Java?
Use Period.between(birthDate, LocalDate.now()).getYears(). Period counts complete years, months and days on the calendar, so a person's age changes exactly on their birthday, including for people born on 29 February.
Which date type should I store in a database?
Store real moments, such as when an order was placed, as an Instant or as a timestamp with a time zone. Store calendar dates without a time, such as birthdays, as a LocalDate. Use LocalDateTime only for local wall-clock times that aren't tied to a place, such as "shops open at 09:00".
How does java.time handle daylight saving time?
ZonedDateTime applies the zone's rules automatically. A local time that doesn't exist because clocks jumped forward is moved past the gap: 01:30 on 29 March 2026 in London becomes 02:30 British Summer Time. Adding Duration.ofHours(24) and Period.ofDays(1) can also give different results across a clock change.
Java Dates Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the code so it creates the date 25 December 2024.
LocalDate xmas = LocalDate.(2024, 12, 25);
Which class holds a date with no time?
What does this print?
LocalDate d = LocalDate.of(2024, 1, 31).plusMonths(1);
System.out.println(d);
Which gives the total number of days between two dates?
What should you store for "when was this order created?" in a system used worldwide?
Java Dates Quiz
5 questions to check what you learned.
In
LocalDate.of(2024, 3, 1), which month is it?- February
- March
- April
- January
What does
MMMMshow in a date pattern?- Minutes
- The month number
- Milliseconds
- The full month name, such as July
Why does
d.plusDays(1)not change d?- It's a bug
- plusDays needs a semicolon
- java.time objects are immutable, so the method returns a new date
- It only works on LocalDateTime
Which exception does parsing badly formatted text throw?
- DateTimeParseException
- NumberFormatException
- IOException
- NullPointerException
What does
withZoneSameInstantdo?- Changes the clock time but keeps the zone
- Removes the time zone
- Shows the same moment in a different time zone
- Rounds to the nearest hour