Course topics

By WebNest Studio Engineering Team

Java - Core Tutorial

Date and Time API

Java 8 introduced the java.time package to replace the old, error-prone java.util.Date and java.util.Calendar classes. The new API is modeled closely on the well-regarded Joda-Time library and fixes long-standing problems: the old classes were mutable, not thread-safe, used confusing zero-based months, and mixed date, time, and timezone concerns into a small number of overloaded classes.

Why it matters

Dates appear in almost every application: due dates, bookings, subscriptions, reports. The old Date and Calendar classes were mutable and easy to misuse, and they still appear in legacy code. The java.time API is what you should write today, and its immutability and clear class names remove most date bugs.

Core Classes

java.time splits date and time concerns into focused, purpose-built classes:

  • LocalDate — a date without time or timezone, such as 2026-09-21.
  • LocalTime — a time without date or timezone, such as 14:30:00.
  • LocalDateTime — a combined date and time, still without timezone.
  • Period — a date-based amount of time (years, months, days), used for differences between LocalDate values.
  • Duration — a time-based amount (hours, minutes, seconds, nanoseconds), used for differences between time-based values.
  • DateTimeFormatter — formats and parses date/time objects using a pattern, replacing the old and not-thread-safe SimpleDateFormat.

Immutability

Every class in java.time is immutable: methods like plusDays() or minusMonths() return a brand-new object with the adjusted value rather than modifying the original in place. This makes the classes inherently thread-safe and eliminates a whole category of bugs where a shared Date object was silently mutated by one part of a program and unexpectedly affected another.

Example

LocalDate, Period, and DateTimeFormatter working together

Java
import java.time.LocalDate;
import java.time.Period;
import java.time.format.DateTimeFormatter;

public class DateTimeDemo {
    public static void main(String[] args) {
        LocalDate today = LocalDate.of(2026, 9, 21);
        LocalDate nextMonth = today.plusMonths(1); // returns a new LocalDate, today is unchanged

        System.out.println("Today: " + today);
        System.out.println("Next month: " + nextMonth);

        LocalDate birthday = LocalDate.of(2000, 5, 15);
        Period age = Period.between(birthday, today);
        System.out.println("Age: " + age.getYears() + " years, " + age.getMonths() + " months");

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy");
        System.out.println(today.format(formatter));
    }
}
Output
Today: 2026-09-21
Next month: 2026-10-21
Age: 26 years, 4 months
21-Sep-2026

Common Mistakes

  • Calling today.plusMonths(1) and expecting "today" itself to change — java.time objects are immutable, so the result must be captured in a new variable.
  • Mixing up Period (date-based: years/months/days) with Duration (time-based: hours/minutes/seconds) when measuring an interval.
  • Continuing to use java.util.Date or Calendar in new code out of habit — java.time should be preferred for all new development.
  • Forgetting that LocalDate and LocalTime carry no timezone information — ZonedDateTime is needed when timezone matters.

Exercise

Starting from 31 January 2024, add one month and print the result, then print that date in the pattern dd/MM/yyyy. Finally print the number of days from 1 January 2024 to 1 March 2024.

Expected output: 2024-02-29, 29/02/2024, 60 (one per line)

Java · starter code
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class DatePractice {
    public static void main(String[] args) {
        LocalDate start = LocalDate.of(2024, 1, 31);

        // TODO: add one month and print the new date
        // TODO: print the new date formatted as dd/MM/yyyy
        // TODO: print the days between 2024-01-01 and 2024-03-01
    }
}
Show hints
  • plusMonths(1) returns a new date. February has no day 31, so the result is the last valid day of the month.
  • ChronoUnit.DAYS.between(from, to) returns the number of days as a long.
Show solution
Java · solution
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class DatePractice {
    public static void main(String[] args) {
        LocalDate start = LocalDate.of(2024, 1, 31);

        LocalDate next = start.plusMonths(1);
        System.out.println(next); // 2024-02-29 (2024 is a leap year)

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
        System.out.println(next.format(formatter)); // 29/02/2024

        long days = ChronoUnit.DAYS.between(LocalDate.of(2024, 1, 1), LocalDate.of(2024, 3, 1));
        System.out.println(days); // 60
    }
}

Quick quiz

1. After LocalDate d = LocalDate.of(2024, 1, 1); d.plusDays(5); what is the value of d?

  • A. 2024-01-06
  • B. 2024-01-01
  • C. 2024-01-05
  • D. It throws an exception
Show answer

B. LocalDate is immutable. plusDays returns a new object, and here the result is discarded.

2. Which class measures an amount of time in hours, minutes and seconds?

  • A. Period
  • B. LocalDate
  • C. Duration
  • D. DateTimeFormatter
Show answer

C. Duration is time-based. Period is date-based: years, months and days.

3. Which class should represent a date of birth?

  • A. LocalDateTime
  • B. Instant
  • C. ZonedDateTime
  • D. LocalDate
Show answer

D. A date of birth has no time of day and no time zone.

Interview questions

Why was the java.time API introduced?

Show answer

java.util.Date and Calendar are mutable, so a date passed to a method can be changed by it, and they are not thread-safe; SimpleDateFormat in particular fails when shared between threads. Their design is also confusing: months start at 0 and a Date is really a timestamp. The java.time classes are immutable, thread-safe, and separate the concepts of date, time, timestamp and time zone into different classes.

What is the difference between LocalDateTime, ZonedDateTime and Instant?

Show answer

LocalDateTime is a date and time with no time zone, like the reading on a wall clock; it does not identify a single moment. ZonedDateTime adds a time zone, so it does. Instant is a point on the UTC timeline, counted from 1 January 1970, and is the usual choice for storing when something happened.

Key Points to Remember

  • java.time (LocalDate, LocalTime, LocalDateTime, Period, Duration, DateTimeFormatter) replaces the legacy Date and Calendar classes.
  • All java.time classes are immutable — operations return new instances instead of mutating the original.
  • Period measures date-based differences (years/months/days); Duration measures time-based differences (hours/minutes/seconds).

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering