Skip to content

Lesson 04 · Dates & Times — the java.time API

Objectives

After this lesson you will be able to:

  • Choose the right java.time type: LocalDate, LocalTime, LocalDateTime, Instant, ZonedDateTime.
  • Create and fluently manipulate them, remembering they are immutable.
  • Tell Period (date-based) from Duration (time-based) and use each correctly.
  • Parse and format with DateTimeFormatter, and avoid the exam's date-math traps.

The core types

The modern API lives in java.time (Java 8+, JSR-310) and replaces the error-prone legacy java.util.Date/Calendar. All its types are immutable and thread-safe. There are no public constructors — you create values with static factory methods (now, of, parse).

TypeHoldsExample
LocalDatea date, no time, no zone2026-06-19
LocalTimea time, no date, no zone10:15:30
LocalDateTimedate + time, no zone2026-06-19T10:15
ZonedDateTimedate + time + zone2026-06-19T10:15+02:00[Europe/Paris]
Instanta point on the UTC timeline (epoch)2026-06-19T08:15:30Z
java
LocalDate d  = LocalDate.of(2026, 6, 19);     // month is 1-based: 6 == June
LocalDate d2 = LocalDate.of(2026, Month.JUNE, 19);
LocalTime t  = LocalTime.of(10, 15, 30);
LocalDateTime dt = LocalDateTime.of(d, t);
Instant now  = Instant.now();                  // always UTC

Exam trap

Unlike the legacy Calendar, months are 1-based here (1 = January, 12 = December). Passing an out-of-range value throws DateTimeException at runtime:

java
LocalDate.of(2026, 13, 1);   // DateTimeException: Invalid value for MonthOfYear
LocalTime.of(24, 0);         // DateTimeException: Invalid value for HourOfDay (0–23)

Immutability & fluent manipulation

Every "change" returns a new object; the original is untouched. Forgetting to assign the result is the #1 mistake.

java
LocalDate d = LocalDate.of(2026, 1, 31);
d.plusDays(1);                 // returns 2026-02-01 — but result is DISCARDED
LocalDate next = d.plusDays(1);// correct: capture the new value

The fluent methods come in families: plusX / minusX, withX (set one field), and getX:

java
LocalDate end = LocalDate.of(2026, 1, 31)
        .plusMonths(1)         // 2026-02-28 — clamps to a valid day, no Feb 31!
        .withDayOfMonth(15);   // 2026-02-15
boolean leap = LocalDate.of(2024, 1, 1).isLeapYear();   // true
DayOfWeek dow = end.getDayOfWeek();

Gotcha

plusMonths/plusYears clamp to the last valid day of the target month rather than overflowing: Jan 31 + 1 month is Feb 28 (or Feb 29 in a leap year), not Mar 3.

Period vs Duration

Both implement TemporalAmount, but they measure different things:

  • Period — a date-based amount in years / months / days. Use with LocalDate.
  • Duration — a time-based amount in seconds / nanos (also exposes hours/minutes). Use with time-bearing types (LocalTime, Instant, LocalDateTime).
java
Period p = Period.of(1, 2, 3);                 // 1 year, 2 months, 3 days
Period gap = Period.between(LocalDate.of(2026,1,1), LocalDate.of(2026,3,15));  // P2M14D
Duration dur = Duration.ofHours(2).plusMinutes(30);   // PT2H30M
Duration elapsed = Duration.between(LocalTime.of(8,0), LocalTime.of(10,30));   // PT2H30M

Exam trap

Adding a Duration to a LocalDate does not compile — a date has no time fields. And a Period of months added to a date is calendar-aware, not a fixed day count:

java
LocalDate.of(2026,1,1).plus(Duration.ofDays(1));   // does NOT compile
Period.between(LocalDate.of(2026,1,31), LocalDate.of(2026,3,1)).getDays();  // 1, not 29

Beyond the exam

Instant, ZonedDateTime, and zone conversion (ZoneId, atZone, withZoneSameInstant) are worth knowing for real apps, but the 1Z0-830 focuses on LocalDate/LocalTime/LocalDateTime, Period/Duration, and formatting. Know that an Instant is UTC and that ZonedDateTime accounts for daylight-saving offsets.

Parsing & formatting

parse reads a string; format writes one. Both default to ISO-8601 and use a DateTimeFormatter for custom patterns.

java
LocalDate d = LocalDate.parse("2026-06-19");           // ISO by default
DateTimeFormatter f = DateTimeFormatter.ofPattern("dd/MM/yyyy");
String s = d.format(f);                                 // "19/06/2026"
LocalDate back = LocalDate.parse("19/06/2026", f);

Exam trap

Pattern letters are case-sensitive and easy to mix up: MM = month, mm = minute; yyyy = year, hh = 12-hour clock, HH = 24-hour. A value that does not match the pattern (or the wrong type for the formatter) throws DateTimeParseException:

java
LocalDate.parse("2026-06-19", DateTimeFormatter.ofPattern("dd/MM/yyyy"));  // DateTimeParseException

SDET note

Never call LocalDate.now()/Instant.now() directly in code you want to test — inject a java.time.Clock (e.g. Clock.fixed(...)) so "now" is deterministic. Tests that depend on the real clock are flaky and fail at midnight, month boundaries, or in other time zones.

Key Takeaways

  • java.time types are immutable and built via static factories (now/of/parse) — there are no public constructors. Always assign the result of plus/minus/with.
  • Months are 1-based; invalid fields throw DateTimeException. plusMonths clamps to a valid day (Jan 31 + 1 month → Feb 28/29).
  • LocalDate/LocalTime/LocalDateTime carry no zone; Instant is UTC; ZonedDateTime adds a zone.
  • Period is date-based (Y/M/D, with LocalDate); Duration is time-based (H/M/S, with time types). Mixing them with the wrong type fails to compile.
  • Format/parse with DateTimeFormatter; pattern letters are case-sensitive (MM vs mm, HH vs hh); a mismatch throws DateTimeParseException.

Lesson Quiz

Lesson Quiz · Dates & Times0 / 6
  1. What does this print?

    LocalDate d = LocalDate.of(2026, 1, 31);
    d.plusDays(1);
    System.out.println(d);
    • A2026-02-01
    • B2026-01-31
    • C2026-01-32
    • DCompile error
  2. What is the result of LocalDate.of(2026, 1, 31).plusMonths(1) ?

    • A2026-02-31
    • B2026-03-03
    • C2026-02-28
    • DDateTimeException
  3. Which line does NOT compile?

    • ALocalDate.of(2026,1,1).plus(Period.ofDays(1))
    • BLocalDate.of(2026,1,1).plus(Duration.ofDays(1))
    • CLocalTime.of(8,0).plus(Duration.ofHours(2))
    • DLocalDateTime.now().plus(Duration.ofMinutes(5))
  4. What does LocalTime.of(24, 0) do?

    • AMidnight next day
    • B00:00
    • CDateTimeException
    • DCompile error
  5. Which formatter pattern formats June as the month and 30 as minutes correctly?

    // time 14:30 on 2026-06-19
    • A"MM HH:MM"
    • B"mm HH:mm"
    • C"MM HH:mm"
    • D"mm hh:MM"
  6. What does Period.between(LocalDate.of(2026,1,1), LocalDate.of(2026,3,15)) produce?

    • AP2M14D
    • BP74D
    • CPT74H
    • DP0M74D

Next: Module 01 Mini-Exam — the timed, whole-module quiz on the module landing page. Run the matching code in labs/src/main/java/com/jse21/m01_values/DateTimes.java.