Date & Time API
Since Java 8, working with dates and times means reaching for the java.time package. It was designed from scratch to replace the old java.util.Date and java.util.Calendar classes, which had earned a well-deserved reputation as one of the worst parts of the original Java API.
The Core Classes
java.time splits date and time concepts into focused, well-named classes instead of one do-everything Date class.
Class | Represents |
|---|---|
| A date with no time or time zone, e.g. 2026-07-08 |
| A time with no date or time zone, e.g. 14:30:00 |
| A date and time combined, still with no time zone |
| A date and time attached to a specific time zone, e.g. |
| An amount of time measured in seconds/nanoseconds — for time-based amounts |
| An amount of time measured in years/months/days — for date-based amounts |
Creating the core types
import java.time.*;
public class DateTimeBasicsDemo {
public static void main(String[] args) {
LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(1995, Month.JULY, 20);
LocalTime now = LocalTime.now();
LocalDateTime meeting = LocalDateTime.of(2026, 7, 8, 15, 30);
ZonedDateTime zoned = ZonedDateTime.now(ZoneId.of("Asia/Kolkata"));
System.out.println("Today: " + today);
System.out.println("Birthday: " + birthday);
System.out.println("Meeting: " + meeting);
System.out.println("Zoned: " + zoned);
}
}Everything in java.time Is Immutable
Just like String, every class in java.time is immutable. Methods such as .plusDays(), .minusMonths(), or .withYear() never modify the object they are called on — they always return a brand new instance representing the adjusted value, leaving the original untouched.
Bug: the return value is discarded
import java.time.LocalDate;
public class BrokenDateDemo {
public static void main(String[] args) {
LocalDate deadline = LocalDate.of(2026, 7, 8);
deadline.plusDays(7); // BUG: return value is thrown away!
System.out.println(deadline); // still 2026-07-08 — unchanged
}
}2026-07-08
Fix: capture the returned instance
import java.time.LocalDate;
public class FixedDateDemo {
public static void main(String[] args) {
LocalDate deadline = LocalDate.of(2026, 7, 8);
deadline = deadline.plusDays(7); // reassign to the new instance
System.out.println(deadline); // 2026-07-15
}
}2026-07-15
Formatting and Parsing
DateTimeFormatter converts between java.time objects and human readable text in both directions — formatting an object to a String, and parsing a String back into an object.
Formatting and parsing dates
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class DateFormatDemo {
public static void main(String[] args) {
LocalDate date = LocalDate.of(2026, 7, 8);
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("dd-MM-yyyy");
String formatted = date.format(formatter); // LocalDate -> String
System.out.println(formatted);
LocalDate parsed = LocalDate.parse("25-12-2026", formatter); // String -> LocalDate
System.out.println(parsed);
}
}08-07-2026 2026-12-25
Measuring Amounts of Time
Use Period for date-based amounts (years, months, days) and Duration for time-based amounts (hours, minutes, seconds).
Period vs. Duration
import java.time.*;
public class AmountsDemo {
public static void main(String[] args) {
LocalDate start = LocalDate.of(2026, 1, 1);
LocalDate end = LocalDate.of(2026, 7, 8);
Period gap = Period.between(start, end);
System.out.println("Months apart: " + gap.getMonths());
LocalTime open = LocalTime.of(9, 0);
LocalTime close = LocalTime.of(17, 30);
Duration shift = Duration.between(open, close);
System.out.println("Shift hours: " + shift.toHours());
}
}Months apart: 6 Shift hours: 8
java.time(Java 8+) replaces the mutable, confusingDate/Calendarclasses.LocalDate,LocalTime,LocalDateTimeandZonedDateTimecover most everyday needs.Every
java.timetype is immutable — always reassign the result of a.plusX()/.minusX()call.DateTimeFormatterhandles both formatting (object → text) and parsing (text → object).Periodmeasures date-based gaps;Durationmeasures time-based gaps.