Dates & Times (LocalDate, LocalDateTime)
Dates look simple and turn out to be a minefield — leap years, month lengths, time zones. Java's modern java.time package handles all of it with clean, immutable classes. This lesson covers the three you'll use daily: LocalDate , LocalTime , and LocalDateTime .
Learn Dates & Times (LocalDate, LocalDateTime) in our free Java course — a beginner-friendly interactive lesson with worked examples, a practice exercise and…
Part of the free Java course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
Before You Start
You'll need comfort with method calls and imports — everything here lives in java.time . Knowing that objects can be immutable will make the key idea click instantly.
What You'll Learn
The Big Idea — A Stamped Calendar Page
💡 Analogy: A LocalDate is like a single page torn from a calendar — it says "17 June 2026" and nothing else. You can't erase and rewrite it; if you want "one week later" you grab a fresh page. That's immutability: every plusDays hands you a new page and leaves the original exactly as it was. This is what makes java.time safe to pass around without anyone secretly changing your dates.
There are three everyday building blocks: LocalDate (date only), LocalTime (time only), and LocalDateTime (both). "Local" means no time zone — perfect for birthdays, opening hours, and appointments where the zone is understood. We'll stick to these three.
1️⃣ Creating Dates and Times
Use the static of(...) factory methods to build a specific value, or now() for the current moment. Months are 1-based here (June is 6) — a welcome fix from the old Date class where January was 0. You can also use the readable Month.JUNE enum.
Once you have an object, getters pull out the pieces: getYear() , getMonth() , getDayOfMonth() , getDayOfWeek() , and more.
2️⃣ Immutability — the One Rule to Remember
Every java.time object is immutable . A method like plusDays(10) does not change the date — it returns a brand-new date and leaves the original untouched. This is wonderful for safety but trips up everyone at least once.
3️⃣ Comparing and Measuring Gaps
Compare two dates with isBefore , isAfter , and isEqual . To measure the gap, you have two tools:
- Period.between(a, b) — a human breakdown into years, months, and days ("5 months, 16 days").
- ChronoUnit.DAYS.between(a, b) — a total count in one unit ("167 days"). Also MONTHS , YEARS , HOURS ...
4️⃣ Formatting and Parsing
DateTimeFormatter converts in both directions: format turns a date object into a custom String, and parse turns a String back into a date object. You define the shape with a pattern of letters.
Letter
Means
Example
yyyy
4-digit year
2026
MM / MMMM
Month number / name
06 / June
dd
Day of month
17
EEEE
Weekday name
Wednesday
HH:mm
24-hour time
14:05
'text'
Literal text
'at'
🧩 Reorder Challenge
These lines compute and print how many days remain until a holiday. They've been shuffled.
Create both dates first, measure the gap with ChronoUnit.DAYS.between (earlier date first so the result is positive), then print.
🧠 Quick Recall
Predict the output before opening each answer.
Answer: 2026-06-17 — unchanged. plusDays(5) returns a new date that is thrown away because the result isn't captured. LocalDate is immutable. To move the date you'd need d = d.plusDays(5); .
Answer: 2026-02-28 . There's no 31 February, so plusMonths clamps to the last valid day of the target month. (2026 isn't a leap year, so February ends on the 28th.) java.time handles month-length edge cases for you.
Answer: 16 . between counts whole days from start to end — June 17 minus June 1 is 16 days. It's exclusive of the start and inclusive of the end's calendar position, so the gap, not the number of dates, is 16.
🎯 Your Turn #1 — 18th birthday
Given a birth date, compute and print the 18th-birthday date.
🎯 Your Turn #2 — Countdown
Count the days between today and a deadline using ChronoUnit .
🧩 Mini-Challenge — Age calculator
Write an age method that returns whole years between a birth date and today using Period .
Common Errors
- ❌ Ignoring the return value. date.plusDays(1); changes nothing. Fix: assign it: date = date.plusDays(1); .
- ❌ Confusing MM and mm in patterns. mm gives minutes, not months. Fix: use uppercase MM for month, lowercase mm for minutes.
- ❌ Reaching for old Date / Calendar . They're mutable and 0-based for months. Fix: always use java.time in new code.
- ❌ Invalid date values. LocalDate.of(2026, 13, 1) throws DateTimeException . Fix: month is 1–12, day must exist in that month.
- ❌ Parse format mismatch. Parsing "17/06/2026" with the default ISO parser throws. Fix: pass a matching DateTimeFormatter to parse .
- ❌ Confusing Period and Duration. Period is for date units (days/months/years), Duration for time units (hours/seconds). Mixing them won't give the value you expect.
Pro Tips
- 💡 Use LocalDate for dates without a time (birthdays, deadlines) — don't drag a time of 00:00 around if you don't need it.
- 💡 Reach for ZonedDateTime or Instant the moment time zones matter (servers, global users). Local* types deliberately ignore zones.
- 💡 Store an instant, format on display. Keep the raw date/time in your data; only apply a DateTimeFormatter when showing it to a human.
- 💡 Immutability makes these classes thread-safe — you can share one DateTimeFormatter across threads, unlike the old SimpleDateFormat .
📋 Quick Reference
Task
Syntax
Notes
Specific date
LocalDate.of(2026, 6, 17)
Month is 1-based
Today / now
LocalDate.now()
Current date
Add time
d = d.plusDays(7)
Returns a new object
Compare
a.isBefore(b)
Also isAfter, isEqual
Days between
ChronoUnit.DAYS.between(a, b)
Total count, one unit
Human gap
Period.between(a, b)
Years/months/days
Format
d.format(formatter)
DateTimeFormatter.ofPattern
❓ Frequently Asked Questions
🎉 Lesson Complete!
You can now build dates and times with java.time , you understand why every object is immutable (and the bug that causes), and you can add, compare, measure, format, and parse with confidence. You're using the same modern API every professional Java codebase relies on.
Next up: Comparable & Comparator — teaching Java how to sort your own objects.
Practice quiz
In LocalDate.of(2026, 6, 17), how is the month value interpreted?
- 0-based, so 6 is July
- It is the day, not the month
- 1-based, so 6 is June
- It must be a Month enum
Answer: 1-based, so 6 is June. java.time months are 1-based: 6 means June. This fixes the old Date class where January was 0.
After LocalDate d = LocalDate.of(2026,6,17); d.plusDays(5); what does d hold?
- 2026-06-17
- 2026-06-22
- It throws an exception
- null
Answer: 2026-06-17. java.time objects are immutable; plusDays returns a NEW date that is discarded here, so d is unchanged. Verified on Java 21.
What does LocalDate.of(2026, 1, 31).plusMonths(1) produce?
- 2026-02-31
- 2026-03-03
- It throws DateTimeException
- 2026-02-28
Answer: 2026-02-28. There is no 31 February, so plusMonths clamps to the last valid day; 2026 is not a leap year, giving 2026-02-28. Verified on Java 21.
Which class should you use for a date with no time, like a birthday?
- LocalDateTime
- LocalDate
- LocalTime
- Instant
Answer: LocalDate. LocalDate holds a date with no time-of-day, perfect for birthdays and deadlines.
What does ChronoUnit.DAYS.between(LocalDate.of(2026,6,1), LocalDate.of(2026,6,17)) return?
- 16
- 17
- 15
- 1
Answer: 16. between counts whole days from start to end: June 17 minus June 1 is 16. Verified on Java 21.
Which pair measures a human breakdown of years/months/days versus a total count in one unit?
- Duration vs Period
- Instant vs LocalDate
- Period vs ChronoUnit.DAYS.between
- ChronoUnit vs LocalTime
Answer: Period vs ChronoUnit.DAYS.between. Period.between gives a years/months/days breakdown; ChronoUnit.DAYS.between gives a single total count of days.
In a DateTimeFormatter pattern, what is the difference between MM and mm?
- MM is minutes, mm is month
- MM is month, mm is minutes
- Both mean month
- Both mean minutes
Answer: MM is month, mm is minutes. Pattern letters are case-sensitive: uppercase MM is the month, lowercase mm is minutes — a classic source of bugs.
Why prefer java.time over the old Date and Calendar classes?
- java.time is mutable and faster
- Date and Calendar are immutable and thread-safe
- Date supports time zones and java.time does not
- java.time classes are immutable, thread-safe, and use 1-based months
Answer: java.time classes are immutable, thread-safe, and use 1-based months. The modern java.time API is immutable, thread-safe, clearly named, and uses natural 1-based months, unlike the error-prone legacy classes.
What is the correct way to actually move a date forward by one day?
- date.plusDays(1);
- date = date.plusDays(1);
- date.addDays(1);
- date.setDay(date.getDay()+1);
Answer: date = date.plusDays(1);. Because dates are immutable you must capture the returned value: date = date.plusDays(1).
Period measures date-based units; what does Duration measure?
- Years, months, and days
- Only whole days
- Hours, minutes, seconds, and nanos
- Time zones
Answer: Hours, minutes, seconds, and nanos. Duration measures time-based units (hours/minutes/seconds/nanos) and pairs with LocalTime/Instant, while Period covers years/months/days.
Continue this course
- Previous: Packages & Imports
- Next: Comparable & Comparator