Date and Time — LocalDate, LocalTime, ZonedDateTime, Duration, Period, and Formatting
In this tutorial, you will learn about Date and Time. We cover key concepts, practical examples, and best practices to help you master this topic.
Java's java.time package provides a comprehensive, immutable date and time API that replaced the legacy Date and Calendar classes. The old API (java.util.Date, java.util.Calendar, java.text.SimpleDateFormat) was notoriously confusing — months were zero-indexed, Date represented both dates and timestamps, and SimpleDateFormat was not thread-safe.
What You'll Learn
- LocalDate, LocalTime, LocalDateTime for date/time without timezone
- ZonedDateTime for timezone-aware operations
- Duration and Period for measuring time spans
- DateTimeFormatter for Parsing and formatting
Why It Matters
Date and time operations are ubiquitous in business applications: scheduling, logging, analytics, and reporting. Using the modern java.time API eliminates thread-safety issues, unclear APIs, and date calculation bugs.
Real-World Use
Every application handles dates: flight booking (timezone-aware), financial reports (accounting periods), activity logs (timestamps), and expiry dates (tokens, subscriptions).
LocalDate
Date without time or timezone:
LocalDate today = LocalDate.now();
LocalDate specific = LocalDate.of(2026, Month.JUNE, 28);
LocalDate parsed = LocalDate.parse("2026-06-28");
int year = today.getYear(); // 2026
Month month = today.getMonth(); // JUNE
int day = today.getDayOfMonth(); // 28
DayOfWeek dow = today.getDayOfWeek(); // SUNDAY
int dayOfYear = today.getDayOfYear();
// Calculations
LocalDate tomorrow = today.plusDays(1);
LocalDate nextWeek = today.plusWeeks(1);
LocalDate nextMonth = today.plusMonths(1);
LocalDate lastYear = today.minusYears(1);
// Comparison
boolean isBefore = date1.isBefore(date2);
boolean isAfter = date1.isAfter(date2);
boolean isEqual = date1.isEqual(date2);
LocalTime
Time without date or timezone:
LocalTime now = LocalTime.now();
LocalTime specific = LocalTime.of(14, 30, 0); // 2:30 PM
LocalTime parsed = LocalTime.parse("14:30:00");
int hour = now.getHour();
int minute = now.getMinute();
int second = now.getSecond();
// Calculations
LocalTime later = now.plusHours(2);
LocalTime earlier = now.minusMinutes(30);
LocalTime truncated = now.truncatedTo(ChronoUnit.HOURS);
// Comparison
boolean isBefore = time1.isBefore(time2);
LocalDateTime
Combines date and time without timezone:
LocalDateTime now = LocalDateTime.now();
LocalDateTime specific = LocalDateTime.of(2026, Month.JUNE, 28, 14, 30);
LocalDateTime parsed = LocalDateTime.parse("2026-06-28T14:30:00");
// Conversion
LocalDate datePart = now.toLocalDate();
LocalTime timePart = now.toLocalTime();
// Calculations
LocalDateTime nextMonth = now.plusMonths(1);
long daysBetween = ChronoUnit.DAYS.between(start, end);
ZonedDateTime
Time with timezone:
ZonedDateTime now = ZonedDateTime.now(); // system default zone
ZonedDateTime inLondon = ZonedDateTime.now(ZoneId.of("Europe/London"));
ZonedDateTime specific = ZonedDateTime.of(
2026, 6, 28, 14, 30, 0, 0,
ZoneId.of("America/New_York")
);
// Zone conversion
ZonedDateTime nyTime = ZonedDateTime.now(ZoneId.of("America/New_York"));
ZonedDateTime tokyoTime = nyTime.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
// Offset
ZoneOffset offset = nyTime.getOffset(); // -04:00 or -05:00
Available Zone IDs
Set<String> zones = ZoneId.getAvailableZoneIds(); // 600+ zones
Duration and Period
Duration (time-based: hours, minutes, seconds)
Duration fiveHours = Duration.ofHours(5);
Duration halfDay = Duration.ofMinutes(30 * 24);
long minutes = fiveHours.toMinutes(); // 300
Duration between = Duration.between(startTime, endTime);
Period (date-based: years, months, days)
Period twoMonths = Period.ofMonths(2);
Period between = Period.between(startDate, endDate);
int years = between.getYears();
int months = between.getMonths();
int days = between.getDays();
ChronoUnit
long hoursBetween = ChronoUnit.HOURS.between(start, end);
long daysBetween = ChronoUnit.DAYS.between(start, end);
DateTimeFormatter
Formatting
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter iso = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
System.out.println(now.format(iso)); // 2026-06-28T14:30:00
DateTimeFormatter custom = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
System.out.println(now.format(custom)); // 28/06/2026 14:30
DateTimeFormatter fullDate = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL);
System.out.println(now.format(fullDate)); // Sunday, June 28, 2026 (locale-dependent)
Common Patterns
| Pattern | Output |
|---|---|
yyyy-MM-dd |
2026-06-28 |
dd/MM/yyyy |
28/06/2026 |
HH:mm:ss |
14:30:00 |
EEE, MMM d, yyyy |
Sun, Jun 28, 2026 |
hh:mm a |
02:30 PM |
Parsing
LocalDate date = LocalDate.parse("28/06/2026",
DateTimeFormatter.ofPattern("dd/MM/yyyy"));
Legacy Compatibility
Convert between old and new APIs:
// Date to Instant
Date legacy = new Date();
Instant instant = legacy.toInstant();
// Calendar to Instant
Calendar cal = Calendar.getInstance();
Instant fromCal = cal.toInstant();
// Instant to LocalDateTime
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
Common Mistakes
- Using
LocalDateTimewhen you need timezone-awareness.LocalDateTimeis a plain date+time without zone. For world-wide apps, useZonedDateTime. - Assuming
Period.between()returns total days.Periodgives years, months, days — not total days. For total days, useChronoUnit.DAYS.between(). - Using
SimpleDateFormatin multi-threaded code.SimpleDateFormatis not thread-safe.DateTimeFormatteris immutable and thread-safe. - Parsing with wrong format.
LocalDate.parse("28/06/2026")throwsDateTimeParseException— the default format isyyyy-MM-dd. - Forgetting
Monthis 1-indexed. Unlike the oldCalendarwhere January was 0,Month.JANUARYis 1.
Practice Questions
1. What is the difference between LocalDate and ZonedDateTime?
LocalDate is a date without time or timezone. ZonedDateTime includes time and full timezone rules (including DST).
2. What is the difference between Duration and Period?
Duration is time-based (hours, minutes, seconds, nanoseconds). Period is date-based (years, months, days).
3. Is DateTimeFormatter thread-safe?
Yes. DateTimeFormatter is immutable and thread-safe, unlike the legacy SimpleDateFormat.
4. How do you convert a java.util.Date to LocalDateTime?
LegacyDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().
5. What does ChronoUnit.DAYS.between() return?
The total number of days between two temporal values, ignoring time components.
Challenge Question:
Write a method long calculateAgeInDays(int year, int month, int day) that returns the number of days between that date and today. Then write a method String nextFriday13th() that returns the date of the next Friday the 13th after today. Use ZonedDateTime for both.
FAQ
Mini Project
Write a program DateTimeDemo.java that:
- Prints today's date, current time, and current date-time
- Calculates the user's age in years, months, and days from their birthdate
- Shows the current time in London, New York, Tokyo, and Sydney
- Formats today's date in 5 different formats (ISO, custom, full, medium, short)
- Calculates the duration until the next New Year (January 1, next year) in days, hours, minutes, and seconds
- Parses a date string "July 4, 2026" and prints it back formatted as "2026-07-04"
- Demonstrates DST-safe arithmetic with
ZonedDateTime
What's Next
Date and time are essential, but how do you get input from users? Lesson 31 covers Scanner and Basic I/O — reading input with Scanner, using Console for password input, working with System.in/out/err, and printf-style formatting.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro