- Master LocalDate, LocalTime, and LocalDateTime classes
- Learn date arithmetic with Period and Duration
- Understand time zones with ZonedDateTime and formatting
Date and Time API (java.time)
Dates and times are everywhere—scheduling appointments, calculating age, logging events, handling time zones. Java 8 introduced a redesigned API in java.time that makes this stuff way easier.
Why did we need it?
The Problems with the Old API
The original java.util.Date and java.util.Calendar from Java 1.0/1.1 had fundamental flaws:
1. Mutability Issues
Date was mutable—its value could change after creation. Nightmare for multi-threaded apps.
// Old API problem: Date is mutable
Date date = new Date();
someMethod(date); // This method might modify our date!
2. Confusing Design
The API had many confusing aspects:
- Months were zero-indexed: January was 0, December was 11
- Years were offset: The year 2024 was represented as 124 (2024 - 1900)
- Date contained time: Despite its name,
Dateactually held date AND time
3. No Time Zone Support
Handling time zones with the old API was complex and error-prone. The Date class stored only a timestamp, with no concept of time zone attached.
4. Not Thread-Safe
SimpleDateFormat, used for parsing and formatting, was not thread-safe, leading to subtle bugs in concurrent applications.
The New java.time API
The new API, inspired by the popular Joda-Time library, solves all these problems:
- Immutable: All classes are immutable and thread-safe
- Clear naming: Classes clearly indicate what they represent
- Intuitive: January is 1, December is 12
- Comprehensive: Built-in support for time zones, durations, and periods
Core Concepts: Choosing the Right Class
The java.time package provides different classes for different use cases. Choosing the right one is important:
| Class | What it Represents | Example Use Case |
|---|---|---|
LocalDate |
Date only (no time) | Birthdays, holidays |
LocalTime |
Time only (no date) | Store opening hours, alarm time |
LocalDateTime |
Date and time (no zone) | Meeting time, event timestamp |
ZonedDateTime |
Date, time, and time zone | International scheduling |
Instant |
Machine timestamp | Logging, database timestamps |
The "Local" prefix means the class has no time zone information - it represents a date or time as seen on a wall clock or calendar, without reference to any specific location.
Working with LocalDate
LocalDate represents a date without time - perfect for birthdays, holidays, or any date-only concept.
Creating LocalDate Objects
There are several ways to create a LocalDate:
// Today's date
LocalDate today = LocalDate.now();
// A specific date (year, month, day)
LocalDate christmas = LocalDate.of(2024, 12, 25);
// Using Month enum for clarity
LocalDate newYear = LocalDate.of(2025, Month.JANUARY, 1);
// Parsing from a string (ISO format: yyyy-MM-dd)
LocalDate parsed = LocalDate.parse("2024-06-15");
Extracting Information
Once you have a date, you can extract various components:
LocalDate date = LocalDate.of(2024, 12, 25);
int year = date.getYear(); // 2024
Month month = date.getMonth(); // DECEMBER
int monthValue = date.getMonthValue(); // 12
int day = date.getDayOfMonth(); // 25
DayOfWeek dow = date.getDayOfWeek(); // WEDNESDAY
int dayOfYear = date.getDayOfYear(); // 360
Useful Checks
LocalDate date = LocalDate.of(2024, 2, 29);
boolean isLeap = date.isLeapYear(); // true (2024 is a leap year)
int daysInMonth = date.lengthOfMonth(); // 29 (February in leap year)
int daysInYear = date.lengthOfYear(); // 366
Working with LocalTime
LocalTime represents a time without a date - useful for recurring events, store hours, or alarm settings.
// Current time
LocalTime now = LocalTime.now();
// Specific times
LocalTime morning = LocalTime.of(9, 30); // 09:30
LocalTime precise = LocalTime.of(14, 30, 45); // 14:30:45
LocalTime withNanos = LocalTime.of(14, 30, 45, 123456789);
// Special constants
LocalTime midnight = LocalTime.MIDNIGHT; // 00:00
LocalTime noon = LocalTime.NOON; // 12:00
Working with LocalDateTime
When you need both date and time but don't care about time zones (like for a local event), use LocalDateTime:
// Current date and time
LocalDateTime now = LocalDateTime.now();
// Specific date and time
LocalDateTime meeting = LocalDateTime.of(2024, 12, 15, 10, 30);
// Combining LocalDate and LocalTime
LocalDate date = LocalDate.of(2024, 12, 31);
LocalTime time = LocalTime.of(23, 59, 59);
LocalDateTime newYearsEve = LocalDateTime.of(date, time);
// Or using the at methods
LocalDateTime combined = date.atTime(time);
LocalDateTime alsoWorks = time.atDate(date);
Date Arithmetic
One of the biggest improvements in the new API is how easy it is to perform date calculations.
Adding and Subtracting
All classes are immutable, so operations return new objects:
LocalDate today = LocalDate.now();
// Adding
LocalDate tomorrow = today.plusDays(1);
LocalDate nextWeek = today.plusWeeks(1);
LocalDate nextMonth = today.plusMonths(1);
LocalDate nextYear = today.plusYears(1);
// Subtracting
LocalDate yesterday = today.minusDays(1);
LocalDate lastMonth = today.minusMonths(1);
Period: Date-Based Amounts
Period represents an amount of time in years, months, and days - useful for human-scale durations:
// Create periods
Period oneYear = Period.ofYears(1);
Period twoMonths = Period.ofMonths(2);
Period tenDays = Period.ofDays(10);
Period complex = Period.of(1, 2, 15); // 1 year, 2 months, 15 days
// Calculate period between dates
LocalDate start = LocalDate.of(2024, 1, 1);
LocalDate end = LocalDate.of(2024, 12, 31);
Period between = Period.between(start, end);
// Result: 11 months, 30 days (not 365 days!)
// Apply period to a date
LocalDate future = today.plus(Period.ofMonths(3));
Important: Period gives you the logical difference (years, months, days), not the total number of days.
Duration: Time-Based Amounts
Duration represents time in seconds and nanoseconds - useful for measuring elapsed time:
// Create durations
Duration oneHour = Duration.ofHours(1);
Duration thirtyMinutes = Duration.ofMinutes(30);
Duration fiveSeconds = Duration.ofSeconds(5);
// Calculate duration between times
LocalTime start = LocalTime.of(9, 0);
LocalTime end = LocalTime.of(17, 30);
Duration workDay = Duration.between(start, end);
long hours = workDay.toHours(); // 8
long minutes = workDay.toMinutes(); // 510
Calculating Differences with ChronoUnit
For getting the total difference in a specific unit, use ChronoUnit:
LocalDate start = LocalDate.of(2024, 1, 1);
LocalDate end = LocalDate.of(2024, 12, 31);
long daysBetween = ChronoUnit.DAYS.between(start, end); // 365
long weeksBetween = ChronoUnit.WEEKS.between(start, end); // 52
long monthsBetween = ChronoUnit.MONTHS.between(start, end); // 11
Time Zones with ZonedDateTime
When you need to work with different time zones - such as scheduling international meetings - use ZonedDateTime:
// Current time in different zones
ZonedDateTime tokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
ZonedDateTime paris = ZonedDateTime.now(ZoneId.of("Europe/Paris"));
ZonedDateTime newYork = ZonedDateTime.now(ZoneId.of("America/New_York"));
// Convert between zones
ZonedDateTime londonNoon = ZonedDateTime.of(
LocalDateTime.of(2024, 7, 15, 12, 0),
ZoneId.of("Europe/London")
);
ZonedDateTime laTime = londonNoon.withZoneSameInstant(ZoneId.of("America/Los_Angeles"));
// London 12:00 → Los Angeles 04:00 (same moment in time)
Formatting and Parsing
The new API provides DateTimeFormatter for converting between dates and strings.
Predefined Formatters
LocalDateTime now = LocalDateTime.now();
String iso = now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
// Result: 2024-12-04T14:30:45.123
Custom Patterns
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
String formatted = now.format(formatter); // 04/12/2024 14:30
// Parsing with custom format
LocalDate parsed = LocalDate.parse("25/12/2024",
DateTimeFormatter.ofPattern("dd/MM/yyyy"));
Common Pattern Letters
| Letter | Meaning | Example |
|---|---|---|
| y | Year | 2024 |
| M | Month | 12 or December |
| d | Day of month | 25 |
| E | Day of week | Wed |
| H | Hour (0-23) | 14 |
| m | Minute | 30 |
| s | Second | 45 |
Practical Example: Age Calculator
Let's put it all together with a practical example:
public static String calculateAge(LocalDate birthDate) {
LocalDate today = LocalDate.now();
Period age = Period.between(birthDate, today);
return String.format("%d years, %d months, %d days",
age.getYears(), age.getMonths(), age.getDays());
}
// Usage
LocalDate birthDate = LocalDate.of(1990, 5, 15);
System.out.println(calculateAge(birthDate));
// Output: 34 years, 6 months, 20 days
The java.time API fixes everything wrong with Date/Calendar. Objects are immutable and thread-safe. January is 1, not 0. Time zones actually work with ZonedDateTime. Date arithmetic is simple with plus/minus methods. Use LocalDate for dates, LocalTime for times, LocalDateTime for both. Use ZonedDateTime when time zones matter. All objects are immutable—operations return new objects.
