Before Java 8, working with dates and times in Java was painful. The original java.util.Date and java.util.Calendar classes were introduced in JDK 1.0 and 1.1 respectively, and they were riddled with design flaws that caused bugs in virtually every codebase.
| Problem | Description | Example |
|---|---|---|
| Mutable | Date objects can be modified after creation. If you pass a Date to a method, that method can change your date. | date.setTime(0) silently changes the date everywhere it is referenced |
| Not thread-safe | SimpleDateFormat is not thread-safe. Using it from multiple threads causes corrupted output or exceptions. | Shared SimpleDateFormat in a web server causes random NumberFormatException |
| Months start at 0 | January is month 0, December is month 11. This causes off-by-one bugs constantly. | new Date(2024, 1, 15) is actually February 15th, not January |
| Year offset from 1900 | The year in Date(year, month, day) is offset from 1900. Year 2024 is represented as 124. |
new Date(2024, 0, 1) is actually year 3924 |
| Poor API design | Date represents both a date and a time. There is no way to represent just a date or just a time. | No clean way to say “March 15th” without a time component |
| Time zone confusion | Date stores a UTC timestamp but toString() converts to the local time zone. Calendar’s time zone handling is inconsistent. |
date.toString() shows local time, but date.getTime() returns UTC millis |
| SQL Date mess | java.sql.Date extends java.util.Date but throws exceptions if you call time methods. Inheritance is broken. |
sqlDate.getHours() throws IllegalArgumentException |
import java.util.Date;
import java.util.Calendar;
import java.text.SimpleDateFormat;
public class OldDateProblems {
public static void main(String[] args) {
// Problem 1: Months start at 0
Calendar cal = Calendar.getInstance();
cal.set(2024, 1, 15); // You think this is January 15, but it's FEBRUARY 15!
System.out.println("Month 1 is: " + cal.getTime());
// Output: Month 1 is: Thu Feb 15 ... 2024
// Problem 2: Date is mutable
Date startDate = new Date();
Date endDate = startDate; // Both point to the same object!
endDate.setTime(0); // This ALSO changes startDate
System.out.println("Start date changed to: " + startDate);
// Output: Start date changed to: Thu Jan 01 00:00:00 GMT 1970
// Problem 3: Year offset from 1900
@SuppressWarnings("deprecation")
Date badDate = new Date(2024, 0, 1); // Year 3924, not 2024!
System.out.println("Year 2024? " + badDate);
// Output: Year 2024? Sat Jan 01 ... 3924
// Problem 4: SimpleDateFormat is NOT thread-safe
// This shared formatter will cause bugs in multi-threaded code:
// static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd");
// Thread 1: FORMAT.parse("2024-01-15");
// Thread 2: FORMAT.parse("2024-06-20"); // Can corrupt Thread 1's result!
// Problem 5: No concept of "just a date" or "just a time"
Date appointment = new Date(); // Always includes date AND time AND timezone
// There is no way to say "March 15th" without a time attached
System.out.println("\nAll these problems are solved by java.time (Java 8+)");
}
}
Java 8 introduced the java.time package, designed by Stephen Colebourne (the creator of Joda-Time) under JSR 310. It fixes every problem with the old API by following three core principles:
of(), now(), plus(), minus(), with() make code readable and intuitive.| Class | Represents | Example Value | Use Case |
|---|---|---|---|
LocalDate |
Date without time or timezone | 2024-03-15 |
Birthdays, holidays, deadlines |
LocalTime |
Time without date or timezone | 14:30:00 |
Store opening hours, alarm times |
LocalDateTime |
Date and time without timezone | 2024-03-15T14:30:00 |
Appointments, event timestamps |
ZonedDateTime |
Date, time, and timezone | 2024-03-15T14:30:00+05:30[Asia/Kolkata] |
Flight departures, global meetings |
Instant |
Machine timestamp (UTC) | 2024-03-15T09:00:00Z |
Database timestamps, logs, APIs |
Period |
Date-based amount | P2Y3M5D (2 years, 3 months, 5 days) |
“How old is someone?” |
Duration |
Time-based amount | PT2H30M (2 hours, 30 minutes) |
“How long did this take?” |
DateTimeFormatter |
Formatting and parsing | dd/MM/yyyy HH:mm |
Display dates, parse user input |
Ask yourself: “Does time zone matter?”
LocalDate, LocalTime, or LocalDateTime. These are “local” to wherever the user is.ZonedDateTime. Essential for scheduling across regions.Instant. A single point on the UTC timeline, independent of human calendars.import java.time.*;
public class JavaTimeOverview {
public static void main(String[] args) {
// Each class represents a different level of precision
LocalDate date = LocalDate.now();
LocalTime time = LocalTime.now();
LocalDateTime dateTime = LocalDateTime.now();
ZonedDateTime zonedDateTime = ZonedDateTime.now();
Instant instant = Instant.now();
System.out.println("LocalDate: " + date); // 2024-03-15
System.out.println("LocalTime: " + time); // 14:30:45.123456
System.out.println("LocalDateTime: " + dateTime); // 2024-03-15T14:30:45.123456
System.out.println("ZonedDateTime: " + zonedDateTime); // 2024-03-15T14:30:45.123456-07:00[America/Los_Angeles]
System.out.println("Instant: " + instant); // 2024-03-15T21:30:45.123456Z
// All are immutable -- "modifying" creates a NEW object
LocalDate tomorrow = date.plusDays(1);
System.out.println("\nOriginal date: " + date); // Unchanged!
System.out.println("Tomorrow: " + tomorrow); // New object
}
}
LocalDate represents a date without a time or timezone — just year, month, and day. Think of it as what you would write on a calendar: March 15, 2024. It is the most commonly used date class.
import java.time.LocalDate;
import java.time.Month;
public class LocalDateCreation {
public static void main(String[] args) {
// Current date
LocalDate today = LocalDate.now();
System.out.println("Today: " + today);
// Output: Today: 2024-03-15
// Specific date using of()
LocalDate christmas = LocalDate.of(2024, 12, 25);
System.out.println("Christmas: " + christmas);
// Output: Christmas: 2024-12-25
// Using Month enum (more readable, no off-by-one errors!)
LocalDate newYear = LocalDate.of(2025, Month.JANUARY, 1);
System.out.println("New Year: " + newYear);
// Output: New Year: 2025-01-01
// Parse from string (ISO format: yyyy-MM-dd)
LocalDate parsed = LocalDate.parse("2024-07-04");
System.out.println("Parsed: " + parsed);
// Output: Parsed: 2024-07-04
// Special dates
LocalDate epoch = LocalDate.EPOCH; // 1970-01-01
LocalDate min = LocalDate.MIN; // -999999999-01-01
LocalDate max = LocalDate.MAX; // +999999999-12-31
System.out.println("Epoch: " + epoch);
// Output: Epoch: 1970-01-01
}
}
Since LocalDate is immutable, all manipulation methods return a new LocalDate.
import java.time.LocalDate;
import java.time.Month;
import java.time.DayOfWeek;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
public class LocalDateManipulation {
public static void main(String[] args) {
LocalDate date = LocalDate.of(2024, Month.MARCH, 15);
// Adding and subtracting
System.out.println("Original: " + date); // 2024-03-15
System.out.println("+5 days: " + date.plusDays(5)); // 2024-03-20
System.out.println("+2 weeks: " + date.plusWeeks(2)); // 2024-03-29
System.out.println("+3 months: " + date.plusMonths(3)); // 2024-06-15
System.out.println("+1 year: " + date.plusYears(1)); // 2025-03-15
System.out.println("-10 days: " + date.minusDays(10)); // 2024-03-05
// Using with() to set specific fields
System.out.println("\nwithYear(2025): " + date.withYear(2025)); // 2025-03-15
System.out.println("withMonth(1): " + date.withMonth(1)); // 2024-01-15
System.out.println("withDayOfMonth: " + date.withDayOfMonth(1)); // 2024-03-01
// Temporal adjusters -- powerful date adjustments
System.out.println("\nFirst day of month: " + date.with(TemporalAdjusters.firstDayOfMonth()));
// Output: 2024-03-01
System.out.println("Last day of month: " + date.with(TemporalAdjusters.lastDayOfMonth()));
// Output: 2024-03-31
System.out.println("First day of year: " + date.with(TemporalAdjusters.firstDayOfYear()));
// Output: 2024-01-01
System.out.println("Next Monday: " + date.with(TemporalAdjusters.next(DayOfWeek.MONDAY)));
// Output: 2024-03-18
System.out.println("Previous Friday: " + date.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY)));
// Output: 2024-03-08
// Getting information
System.out.println("\nYear: " + date.getYear()); // 2024
System.out.println("Month: " + date.getMonth()); // MARCH
System.out.println("Month value: " + date.getMonthValue()); // 3 (not 2!)
System.out.println("Day of month: " + date.getDayOfMonth()); // 15
System.out.println("Day of week: " + date.getDayOfWeek()); // FRIDAY
System.out.println("Day of year: " + date.getDayOfYear()); // 75
System.out.println("Leap year: " + date.isLeapYear()); // true (2024)
System.out.println("Days in month: " + date.lengthOfMonth()); // 31
System.out.println("Days in year: " + date.lengthOfYear()); // 366
}
}
import java.time.LocalDate;
import java.time.Month;
import java.time.Period;
import java.time.temporal.ChronoUnit;
public class LocalDateComparison {
public static void main(String[] args) {
LocalDate today = LocalDate.of(2024, Month.MARCH, 15);
LocalDate birthday = LocalDate.of(1990, Month.JULY, 20);
LocalDate deadline = LocalDate.of(2024, Month.APRIL, 1);
// Comparison methods
System.out.println("Is today before deadline? " + today.isBefore(deadline)); // true
System.out.println("Is today after birthday? " + today.isAfter(birthday)); // true
System.out.println("Are they equal? " + today.isEqual(today)); // true
// Calculate age
Period age = Period.between(birthday, today);
System.out.println("\nAge: " + age.getYears() + " years, "
+ age.getMonths() + " months, "
+ age.getDays() + " days");
// Output: Age: 33 years, 7 months, 24 days
// Days between dates
long daysBetween = ChronoUnit.DAYS.between(today, deadline);
System.out.println("Days until deadline: " + daysBetween);
// Output: Days until deadline: 17
long monthsBetween = ChronoUnit.MONTHS.between(birthday, today);
System.out.println("Months since birth: " + monthsBetween);
// Output: Months since birth: 403
// Age calculation utility
System.out.println("\n--- Age Calculator ---");
LocalDate[] birthdays = {
LocalDate.of(2000, 1, 1),
LocalDate.of(1985, 6, 15),
LocalDate.of(2010, 12, 25)
};
for (LocalDate bday : birthdays) {
int years = Period.between(bday, today).getYears();
System.out.printf("Born %s -> Age %d%n", bday, years);
}
// Output:
// Born 2000-01-01 -> Age 24
// Born 1985-06-15 -> Age 38
// Born 2010-12-25 -> Age 13
}
}
LocalTime represents a time without a date or timezone — hours, minutes, seconds, and nanoseconds. Think of it as what you see on a clock: 14:30:00. Use it for things like store opening hours, alarm times, or daily schedules.
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
public class LocalTimeDemo {
public static void main(String[] args) {
// Creating LocalTime
LocalTime now = LocalTime.now();
LocalTime morning = LocalTime.of(9, 0); // 09:00
LocalTime afternoon = LocalTime.of(14, 30, 0); // 14:30:00
LocalTime precise = LocalTime.of(10, 15, 30, 500_000_000); // 10:15:30.5
LocalTime parsed = LocalTime.parse("18:45:00"); // 18:45:00
System.out.println("Now: " + now);
System.out.println("Morning: " + morning);
System.out.println("Afternoon: " + afternoon);
System.out.println("Precise: " + precise);
// Special constants
System.out.println("\nMidnight: " + LocalTime.MIDNIGHT); // 00:00
System.out.println("Noon: " + LocalTime.NOON); // 12:00
System.out.println("Min: " + LocalTime.MIN); // 00:00
System.out.println("Max: " + LocalTime.MAX); // 23:59:59.999999999
// Manipulation
LocalTime meetingTime = LocalTime.of(10, 0);
System.out.println("\nMeeting: " + meetingTime);
System.out.println("+2 hours: " + meetingTime.plusHours(2)); // 12:00
System.out.println("+30 mins: " + meetingTime.plusMinutes(30)); // 10:30
System.out.println("-1 hour: " + meetingTime.minusHours(1)); // 09:00
// Getting components
System.out.println("\nHour: " + afternoon.getHour()); // 14
System.out.println("Minute: " + afternoon.getMinute()); // 30
System.out.println("Second: " + afternoon.getSecond()); // 0
// Comparing
System.out.println("\nMorning before afternoon? " + morning.isBefore(afternoon)); // true
System.out.println("Morning after afternoon? " + morning.isAfter(afternoon)); // false
// Time between
long minutesBetween = ChronoUnit.MINUTES.between(morning, afternoon);
System.out.println("Minutes from morning to afternoon: " + minutesBetween);
// Output: Minutes from morning to afternoon: 330
// Business hours check
LocalTime openTime = LocalTime.of(9, 0);
LocalTime closeTime = LocalTime.of(17, 0);
LocalTime checkTime = LocalTime.of(14, 30);
boolean isOpen = !checkTime.isBefore(openTime) && checkTime.isBefore(closeTime);
System.out.println("\nStore open at " + checkTime + "? " + isOpen);
// Output: Store open at 14:30? true
}
}
LocalDateTime combines LocalDate and LocalTime into a single object — a date and time without a timezone. It is the most commonly used class when you need both date and time together, such as for appointments, event timestamps, or log entries in a single-timezone application.
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.temporal.ChronoUnit;
public class LocalDateTimeDemo {
public static void main(String[] args) {
// Creating LocalDateTime
LocalDateTime now = LocalDateTime.now();
LocalDateTime specific = LocalDateTime.of(2024, Month.DECEMBER, 31, 23, 59, 59);
LocalDateTime parsed = LocalDateTime.parse("2024-07-04T10:30:00");
System.out.println("Now: " + now);
System.out.println("Specific: " + specific); // 2024-12-31T23:59:59
System.out.println("Parsed: " + parsed); // 2024-07-04T10:30
// Combining LocalDate and LocalTime
LocalDate date = LocalDate.of(2024, 6, 15);
LocalTime time = LocalTime.of(14, 30);
LocalDateTime combined = LocalDateTime.of(date, time);
System.out.println("Combined: " + combined); // 2024-06-15T14:30
// Alternative: atTime() and atDate()
LocalDateTime fromDate = date.atTime(9, 0); // 2024-06-15T09:00
LocalDateTime fromTime = time.atDate(date); // 2024-06-15T14:30
LocalDateTime startOfDay = date.atStartOfDay(); // 2024-06-15T00:00
System.out.println("Start of day: " + startOfDay);
// Extracting date and time
LocalDate extractedDate = combined.toLocalDate();
LocalTime extractedTime = combined.toLocalTime();
System.out.println("\nExtracted date: " + extractedDate); // 2024-06-15
System.out.println("Extracted time: " + extractedTime); // 14:30
// Manipulation
LocalDateTime event = LocalDateTime.of(2024, 3, 15, 10, 0);
System.out.println("\nEvent: " + event);
System.out.println("+3 days: " + event.plusDays(3));
System.out.println("+2 hours: " + event.plusHours(2));
System.out.println("-30 minutes: " + event.minusMinutes(30));
System.out.println("Set hour to 8: " + event.withHour(8));
// Comparing
LocalDateTime meeting1 = LocalDateTime.of(2024, 3, 15, 10, 0);
LocalDateTime meeting2 = LocalDateTime.of(2024, 3, 15, 14, 0);
System.out.println("\nMeeting1 before Meeting2? " + meeting1.isBefore(meeting2)); // true
long hoursBetween = ChronoUnit.HOURS.between(meeting1, meeting2);
System.out.println("Hours between meetings: " + hoursBetween);
// Output: Hours between meetings: 4
}
}
ZonedDateTime is a date-time with full time zone information. Use it whenever the timezone matters — scheduling meetings across countries, flight departure/arrival times, or any scenario where the same “local time” has different meanings in different regions.
A time zone is identified by a ZoneId like "America/New_York" or "Asia/Tokyo". It encodes the UTC offset AND the daylight saving rules for that region. Do not use fixed offsets like "GMT+5" unless you specifically want to ignore DST.
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Set;
public class ZonedDateTimeDemo {
public static void main(String[] args) {
// Creating ZonedDateTime
ZonedDateTime nowHere = ZonedDateTime.now();
ZonedDateTime nowTokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
ZonedDateTime nowLondon = ZonedDateTime.now(ZoneId.of("Europe/London"));
System.out.println("Here: " + nowHere);
System.out.println("Tokyo: " + nowTokyo);
System.out.println("London: " + nowLondon);
// Specific date-time in a zone
ZonedDateTime meeting = ZonedDateTime.of(
2024, 3, 15, 10, 0, 0, 0,
ZoneId.of("America/New_York")
);
System.out.println("\nMeeting (NY): " + meeting);
// Output: Meeting (NY): 2024-03-15T10:00-04:00[America/New_York]
// Converting between time zones
ZonedDateTime meetingLA = meeting.withZoneSameInstant(ZoneId.of("America/Los_Angeles"));
ZonedDateTime meetingLondon = meeting.withZoneSameInstant(ZoneId.of("Europe/London"));
ZonedDateTime meetingTokyo = meeting.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
ZonedDateTime meetingIndia = meeting.withZoneSameInstant(ZoneId.of("Asia/Kolkata"));
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm z (MMM dd)");
System.out.println("\nMeeting at 10:00 AM New York is:");
System.out.println(" Los Angeles: " + meetingLA.format(fmt));
System.out.println(" London: " + meetingLondon.format(fmt));
System.out.println(" Tokyo: " + meetingTokyo.format(fmt));
System.out.println(" India: " + meetingIndia.format(fmt));
// Output:
// Meeting at 10:00 AM New York is:
// Los Angeles: 07:00 PDT (Mar 15)
// London: 14:00 GMT (Mar 15)
// Tokyo: 23:00 JST (Mar 15)
// India: 19:30 IST (Mar 15)
// withZoneSameLocal vs withZoneSameInstant
// withZoneSameInstant: Same moment, different clock reading
// withZoneSameLocal: Same clock reading, different moment
ZonedDateTime noonNY = ZonedDateTime.of(2024, 6, 15, 12, 0, 0, 0,
ZoneId.of("America/New_York"));
ZonedDateTime sameInstant = noonNY.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
ZonedDateTime sameLocal = noonNY.withZoneSameLocal(ZoneId.of("Asia/Tokyo"));
System.out.println("\nNoon in NY: " + noonNY);
System.out.println("Same instant: " + sameInstant); // Next day 1 AM in Tokyo
System.out.println("Same local time: " + sameLocal); // Noon in Tokyo (different moment!)
// Available zone IDs
Set zones = ZoneId.getAvailableZoneIds();
System.out.println("\nTotal time zones available: " + zones.size());
// Output: Total time zones available: ~600
// Handling Daylight Saving Time
// Spring forward: 2:00 AM -> 3:00 AM (1 hour gap)
ZonedDateTime beforeDST = ZonedDateTime.of(
2024, 3, 10, 1, 30, 0, 0, ZoneId.of("America/New_York"));
ZonedDateTime afterDST = beforeDST.plusHours(1);
System.out.println("\nBefore DST: " + beforeDST); // 01:30 EST
System.out.println("After +1h: " + afterDST); // 03:30 EDT (skips 2:00-3:00!)
}
}
Instant represents a single point on the UTC timeline — a machine timestamp measured in seconds and nanoseconds from the Unix epoch (January 1, 1970, 00:00:00 UTC). It has no concept of human-readable dates, months, or time zones. Think of it as what a computer stores internally when tracking “when something happened.”
Use Instant for:
import java.time.Instant;
import java.time.Duration;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
public class InstantDemo {
public static void main(String[] args) throws InterruptedException {
// Current instant
Instant now = Instant.now();
System.out.println("Now: " + now);
// Output: Now: 2024-03-15T21:30:45.123456Z (always UTC, denoted by Z)
// From epoch
Instant epoch = Instant.EPOCH;
System.out.println("Epoch: " + epoch);
// Output: Epoch: 1970-01-01T00:00:00Z
Instant fromEpochSeconds = Instant.ofEpochSecond(1_700_000_000);
System.out.println("From epoch seconds: " + fromEpochSeconds);
// Output: From epoch seconds: 2023-11-14T22:13:20Z
Instant fromEpochMillis = Instant.ofEpochMilli(System.currentTimeMillis());
System.out.println("From epoch millis: " + fromEpochMillis);
// Parse ISO-8601 string
Instant parsed = Instant.parse("2024-01-15T10:30:00Z");
System.out.println("Parsed: " + parsed);
// Getting epoch values
System.out.println("\nEpoch second: " + now.getEpochSecond());
System.out.println("Nano adjust: " + now.getNano());
// Measuring execution time
System.out.println("\n--- Execution Time ---");
Instant start = Instant.now();
// Simulate work
long sum = 0;
for (int i = 0; i < 10_000_000; i++) {
sum += i;
}
Instant end = Instant.now();
Duration elapsed = Duration.between(start, end);
System.out.println("Sum: " + sum);
System.out.printf("Elapsed: %d ms (%d ns)%n",
elapsed.toMillis(), elapsed.toNanos());
// Output: Elapsed: 15 ms (15234567 ns)
// Comparing instants
Instant past = Instant.now().minus(1, ChronoUnit.HOURS);
Instant future = Instant.now().plus(1, ChronoUnit.HOURS);
System.out.println("\nPast is before now: " + past.isBefore(now)); // true
System.out.println("Future is after now: " + future.isAfter(now)); // true
// Converting Instant to ZonedDateTime (for human display)
ZonedDateTime inNY = now.atZone(ZoneId.of("America/New_York"));
ZonedDateTime inTokyo = now.atZone(ZoneId.of("Asia/Tokyo"));
System.out.println("\nSame instant, different zones:");
System.out.println(" New York: " + inNY);
System.out.println(" Tokyo: " + inTokyo);
}
}
Java 8 provides two classes for representing amounts of time:
Period -- A date-based amount: years, months, and days. Use it to answer "How many years/months/days between two dates?"Duration -- A time-based amount: hours, minutes, seconds, and nanoseconds. Use it to answer "How long did this operation take?"| Feature | Period | Duration |
|---|---|---|
| Measures | Years, months, days | Hours, minutes, seconds, nanos |
| Pair with | LocalDate |
LocalTime, Instant |
| ISO format | P2Y3M5D |
PT2H30M |
| Factory method | Period.between(date1, date2) |
Duration.between(time1, time2) |
| Use case | "Contract expires in 2 years" | "API call took 350ms" |
import java.time.*;
import java.time.temporal.ChronoUnit;
public class PeriodDurationDemo {
public static void main(String[] args) {
// ========== PERIOD (date-based) ==========
System.out.println("=== Period ===");
// Creating periods
Period twoYears = Period.ofYears(2);
Period threeMonths = Period.ofMonths(3);
Period tenDays = Period.ofDays(10);
Period custom = Period.of(1, 6, 15); // 1 year, 6 months, 15 days
System.out.println("Two years: " + twoYears); // P2Y
System.out.println("Three months: " + threeMonths); // P3M
System.out.println("Custom: " + custom); // P1Y6M15D
// Between two dates
LocalDate hired = LocalDate.of(2020, Month.MARCH, 1);
LocalDate today = LocalDate.of(2024, Month.MARCH, 15);
Period tenure = Period.between(hired, today);
System.out.println("\nTenure: " + tenure.getYears() + " years, "
+ tenure.getMonths() + " months, "
+ tenure.getDays() + " days");
// Output: Tenure: 4 years, 0 months, 14 days
// Adding period to a date
LocalDate contractStart = LocalDate.of(2024, 1, 1);
LocalDate contractEnd = contractStart.plus(Period.of(2, 0, 0));
System.out.println("Contract: " + contractStart + " to " + contractEnd);
// Output: Contract: 2024-01-01 to 2026-01-01
// ========== DURATION (time-based) ==========
System.out.println("\n=== Duration ===");
// Creating durations
Duration twoHours = Duration.ofHours(2);
Duration thirtyMinutes = Duration.ofMinutes(30);
Duration fiveSeconds = Duration.ofSeconds(5);
Duration halfSecond = Duration.ofMillis(500);
System.out.println("Two hours: " + twoHours); // PT2H
System.out.println("Thirty minutes: " + thirtyMinutes); // PT30M
System.out.println("Five seconds: " + fiveSeconds); // PT5S
System.out.println("Half second: " + halfSecond); // PT0.5S
// Between two times
LocalTime start = LocalTime.of(9, 0);
LocalTime end = LocalTime.of(17, 30);
Duration workday = Duration.between(start, end);
System.out.println("\nWorkday: " + workday);
System.out.println("In hours: " + workday.toHours()); // 8
System.out.println("In minutes: " + workday.toMinutes()); // 510
System.out.println("In seconds: " + workday.getSeconds()); // 30600
// Between two instants
Instant begin = Instant.parse("2024-03-15T10:00:00Z");
Instant finish = Instant.parse("2024-03-15T10:05:30.500Z");
Duration apiCall = Duration.between(begin, finish);
System.out.println("\nAPI duration: " + apiCall); // PT5M30.5S
System.out.println("In millis: " + apiCall.toMillis()); // 330500
// Manipulation
Duration meeting = Duration.ofHours(1);
System.out.println("\nMeeting: " + meeting);
System.out.println("+30 min: " + meeting.plusMinutes(30)); // PT1H30M
System.out.println("x2: " + meeting.multipliedBy(2)); // PT2H
// Parse ISO-8601
Duration parsed = Duration.parse("PT2H30M");
System.out.println("Parsed: " + parsed); // PT2H30M
// ChronoUnit for total amounts
long totalDays = ChronoUnit.DAYS.between(
LocalDate.of(2024, 1, 1), LocalDate.of(2024, 12, 31));
System.out.println("\nDays in 2024: " + totalDays);
// Output: Days in 2024: 365
}
}
DateTimeFormatter is the replacement for SimpleDateFormat. It is immutable and thread-safe, which means you can safely share a single instance across your entire application. It handles both formatting (date to string) and parsing (string to date).
Java provides several built-in formatters for common ISO formats:
| Formatter | Example Output | Use Case |
|---|---|---|
ISO_LOCAL_DATE |
2024-03-15 |
Standard date format |
ISO_LOCAL_TIME |
14:30:00 |
Standard time format |
ISO_LOCAL_DATE_TIME |
2024-03-15T14:30:00 |
Standard date-time format |
ISO_ZONED_DATE_TIME |
2024-03-15T14:30:00-04:00[America/New_York] |
Date-time with zone |
ISO_INSTANT |
2024-03-15T18:30:00Z |
UTC timestamp |
ISO_DATE |
2024-03-15 or 2024-03-15-04:00 |
Date with optional offset |
RFC_1123_DATE_TIME |
Fri, 15 Mar 2024 14:30:00 -0400 |
HTTP headers |
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Locale;
public class DateTimeFormatterDemo {
public static void main(String[] args) {
LocalDateTime dateTime = LocalDateTime.of(2024, 3, 15, 14, 30, 0);
LocalDate date = dateTime.toLocalDate();
// Predefined formatters
System.out.println("=== Predefined Formatters ===");
System.out.println("ISO_LOCAL_DATE: " + date.format(DateTimeFormatter.ISO_LOCAL_DATE));
System.out.println("ISO_LOCAL_DATE_TIME: " + dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
// Output:
// ISO_LOCAL_DATE: 2024-03-15
// ISO_LOCAL_DATE_TIME: 2024-03-15T14:30:00
// Custom patterns
System.out.println("\n=== Custom Patterns ===");
DateTimeFormatter f1 = DateTimeFormatter.ofPattern("dd/MM/yyyy");
DateTimeFormatter f2 = DateTimeFormatter.ofPattern("MMMM dd, yyyy");
DateTimeFormatter f3 = DateTimeFormatter.ofPattern("MM-dd-yyyy HH:mm:ss");
DateTimeFormatter f4 = DateTimeFormatter.ofPattern("EEE, MMM d, yyyy h:mm a");
DateTimeFormatter f5 = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm");
System.out.println("dd/MM/yyyy: " + date.format(f1));
System.out.println("MMMM dd, yyyy: " + date.format(f2));
System.out.println("MM-dd-yyyy HH:mm:ss: " + dateTime.format(f3));
System.out.println("EEE, MMM d, ...: " + dateTime.format(f4));
System.out.println("yyyy/MM/dd HH:mm: " + dateTime.format(f5));
// Output:
// dd/MM/yyyy: 15/03/2024
// MMMM dd, yyyy: March 15, 2024
// MM-dd-yyyy HH:mm:ss: 03-15-2024 14:30:00
// EEE, MMM d, ...: Fri, Mar 15, 2024 2:30 PM
// yyyy/MM/dd HH:mm: 2024/03/15 14:30
// Localized formatters
System.out.println("\n=== Localized Formatters ===");
DateTimeFormatter french = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL)
.withLocale(Locale.FRENCH);
DateTimeFormatter german = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)
.withLocale(Locale.GERMAN);
DateTimeFormatter japanese = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)
.withLocale(Locale.JAPANESE);
System.out.println("French (FULL): " + date.format(french));
System.out.println("German (LONG): " + date.format(german));
System.out.println("Japanese (MEDIUM):" + date.format(japanese));
// Output (varies by JDK):
// French (FULL): vendredi 15 mars 2024
// German (LONG): 15. Marz 2024
// Japanese (MEDIUM):2024/03/15
// Parsing strings to dates
System.out.println("\n=== Parsing ===");
LocalDate parsed1 = LocalDate.parse("2024-03-15"); // ISO format, no formatter needed
LocalDate parsed2 = LocalDate.parse("15/03/2024", f1);
LocalDate parsed3 = LocalDate.parse("March 15, 2024", f2);
LocalDateTime parsed4 = LocalDateTime.parse("03-15-2024 14:30:00", f3);
System.out.println("Parsed ISO: " + parsed1);
System.out.println("Parsed dd/MM: " + parsed2);
System.out.println("Parsed Month: " + parsed3);
System.out.println("Parsed full: " + parsed4);
}
}
| Symbol | Meaning | Example |
|---|---|---|
y |
Year | 2024 (yyyy), 24 (yy) |
M |
Month | 3 (M), 03 (MM), Mar (MMM), March (MMMM) |
d |
Day of month | 5 (d), 05 (dd) |
E |
Day of week | Fri (EEE), Friday (EEEE) |
H |
Hour (0-23) | 14 (HH) |
h |
Hour (1-12) | 2 (h), 02 (hh) |
m |
Minute | 30 (mm) |
s |
Second | 45 (ss) |
S |
Fraction of second | 123 (SSS) |
a |
AM/PM | PM |
z |
Time zone name | PST, PDT |
Z |
Time zone offset | -0800 |
V |
Time zone ID | America/Los_Angeles (VV) |
If you are working with legacy code that uses java.util.Date or java.util.Calendar, Java 8 provides bridge methods to convert to and from the new API. The key conversion point is Instant -- all legacy classes can convert to an Instant, and from there you can get any java.time class.
import java.time.*;
import java.util.Date;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.sql.Timestamp;
public class LegacyConversion {
public static void main(String[] args) {
// ========== java.util.Date <-> java.time ==========
System.out.println("=== java.util.Date Conversion ===");
// Date -> Instant -> LocalDateTime
Date oldDate = new Date();
Instant instant = oldDate.toInstant();
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
LocalDate ld = ldt.toLocalDate();
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
System.out.println("Old Date: " + oldDate);
System.out.println("Instant: " + instant);
System.out.println("LocalDateTime: " + ldt);
System.out.println("LocalDate: " + ld);
System.out.println("ZonedDateTime: " + zdt);
// LocalDateTime -> Date (reverse)
LocalDateTime newDateTime = LocalDateTime.of(2024, 6, 15, 14, 30);
Date backToDate = Date.from(newDateTime.atZone(ZoneId.systemDefault()).toInstant());
System.out.println("\nBack to Date: " + backToDate);
// ========== Calendar <-> java.time ==========
System.out.println("\n=== Calendar Conversion ===");
// Calendar -> ZonedDateTime
Calendar calendar = Calendar.getInstance();
if (calendar instanceof GregorianCalendar gc) {
ZonedDateTime fromCal = gc.toZonedDateTime();
System.out.println("From Calendar: " + fromCal);
}
// ZonedDateTime -> Calendar
ZonedDateTime zonedNow = ZonedDateTime.now();
GregorianCalendar backToCal = GregorianCalendar.from(zonedNow);
System.out.println("Back to Cal: " + backToCal.getTime());
// ========== TimeZone <-> ZoneId ==========
System.out.println("\n=== TimeZone Conversion ===");
TimeZone oldTZ = TimeZone.getTimeZone("America/New_York");
ZoneId newZoneId = oldTZ.toZoneId();
TimeZone backToTZ = TimeZone.getTimeZone(newZoneId);
System.out.println("Old TimeZone: " + oldTZ.getID());
System.out.println("New ZoneId: " + newZoneId);
// ========== java.sql.Timestamp <-> java.time ==========
System.out.println("\n=== SQL Timestamp Conversion ===");
// Timestamp -> LocalDateTime (no timezone conversion)
Timestamp sqlTimestamp = Timestamp.valueOf("2024-03-15 14:30:00");
LocalDateTime fromTimestamp = sqlTimestamp.toLocalDateTime();
System.out.println("From Timestamp: " + fromTimestamp);
// LocalDateTime -> Timestamp
Timestamp backToTimestamp = Timestamp.valueOf(newDateTime);
System.out.println("Back to Timestamp: " + backToTimestamp);
// Timestamp -> Instant
Instant fromTimestampInstant = sqlTimestamp.toInstant();
System.out.println("Timestamp Instant: " + fromTimestampInstant);
}
}
| Old API | New API | Conversion Method |
|---|---|---|
java.util.Date |
Instant |
date.toInstant() |
Instant |
java.util.Date |
Date.from(instant) |
java.util.Date |
LocalDate |
date.toInstant().atZone(zone).toLocalDate() |
LocalDate |
java.util.Date |
Date.from(localDate.atStartOfDay(zone).toInstant()) |
GregorianCalendar |
ZonedDateTime |
gc.toZonedDateTime() |
ZonedDateTime |
GregorianCalendar |
GregorianCalendar.from(zdt) |
java.sql.Timestamp |
LocalDateTime |
ts.toLocalDateTime() |
LocalDateTime |
java.sql.Timestamp |
Timestamp.valueOf(ldt) |
java.sql.Date |
LocalDate |
sqlDate.toLocalDate() |
LocalDate |
java.sql.Date |
java.sql.Date.valueOf(localDate) |
TimeZone |
ZoneId |
timeZone.toZoneId() |
SimpleDateFormat |
DateTimeFormatter |
No direct conversion; rewrite pattern |
This example demonstrates a realistic event scheduling system that uses every major class from the java.time package. It handles creating events across time zones, calculating durations, formatting for different locales, and checking for schedule conflicts.
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;
public class EventSchedulingSystem {
// Immutable Event record
record Event(
String name,
ZonedDateTime start,
Duration duration,
ZoneId creatorZone
) {
ZonedDateTime end() {
return start.plus(duration);
}
boolean conflictsWith(Event other) {
return this.start.isBefore(other.end()) && other.start.isBefore(this.end());
}
String formatForZone(ZoneId zone) {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("EEE, MMM d yyyy 'at' h:mm a z");
ZonedDateTime inZone = start.withZoneSameInstant(zone);
return name + ": " + inZone.format(fmt)
+ " (" + duration.toHours() + "h " + (duration.toMinutesPart()) + "m)";
}
}
public static void main(String[] args) {
System.out.println("=== Event Scheduling System ===\n");
// Define time zones for participants
ZoneId newYork = ZoneId.of("America/New_York");
ZoneId london = ZoneId.of("Europe/London");
ZoneId tokyo = ZoneId.of("Asia/Tokyo");
ZoneId india = ZoneId.of("Asia/Kolkata");
// ---- Create Events ----
System.out.println("--- Creating Events ---");
// Event 1: Team standup at 9 AM New York time
Event standup = new Event(
"Team Standup",
ZonedDateTime.of(2024, 3, 18, 9, 0, 0, 0, newYork),
Duration.ofMinutes(30),
newYork
);
// Event 2: Client demo at 2 PM London time
Event clientDemo = new Event(
"Client Demo",
ZonedDateTime.of(2024, 3, 18, 14, 0, 0, 0, london),
Duration.ofHours(1).plusMinutes(30),
london
);
// Event 3: Sprint planning at 10 AM Tokyo time
Event sprintPlanning = new Event(
"Sprint Planning",
ZonedDateTime.of(2024, 3, 18, 10, 0, 0, 0, tokyo),
Duration.ofHours(2),
tokyo
);
List events = List.of(standup, clientDemo, sprintPlanning);
// ---- Display Events in All Time Zones ----
System.out.println("\n--- Events in Each Time Zone ---");
List zones = List.of(newYork, london, tokyo, india);
for (ZoneId zone : zones) {
System.out.println("\n" + zone.getId() + ":");
for (Event event : events) {
System.out.println(" " + event.formatForZone(zone));
}
}
// ---- Check for Conflicts ----
System.out.println("\n--- Conflict Detection ---");
for (int i = 0; i < events.size(); i++) {
for (int j = i + 1; j < events.size(); j++) {
Event e1 = events.get(i);
Event e2 = events.get(j);
if (e1.conflictsWith(e2)) {
System.out.println("CONFLICT: " + e1.name() + " overlaps with " + e2.name());
} else {
System.out.println("OK: " + e1.name() + " and " + e2.name() + " do not overlap");
}
}
}
// ---- Calculate Time Until Events ----
System.out.println("\n--- Time Until Events (from now) ---");
ZonedDateTime now = ZonedDateTime.of(2024, 3, 15, 12, 0, 0, 0, newYork);
for (Event event : events) {
Duration until = Duration.between(now.toInstant(), event.start().toInstant());
long days = until.toDays();
long hours = until.toHoursPart();
long minutes = until.toMinutesPart();
System.out.printf(" %s: %d days, %d hours, %d minutes%n",
event.name(), days, hours, minutes);
}
// ---- Find Next Available Slot ----
System.out.println("\n--- Next Available Slot ---");
LocalDate meetingDate = LocalDate.of(2024, 3, 18);
Duration meetingLength = Duration.ofHours(1);
// Check each hour of the business day (9 AM - 5 PM New York)
LocalTime checkStart = LocalTime.of(9, 0);
LocalTime checkEnd = LocalTime.of(17, 0);
LocalTime candidate = checkStart;
while (candidate.plus(meetingLength).isBefore(checkEnd)
|| candidate.plus(meetingLength).equals(checkEnd)) {
ZonedDateTime candidateStart = ZonedDateTime.of(meetingDate, candidate, newYork);
ZonedDateTime candidateEnd = candidateStart.plus(meetingLength);
boolean hasConflict = false;
for (Event event : events) {
ZonedDateTime eventStartNY = event.start().withZoneSameInstant(newYork);
ZonedDateTime eventEndNY = event.end().withZoneSameInstant(newYork);
if (candidateStart.isBefore(eventEndNY) && eventStartNY.isBefore(candidateEnd)) {
hasConflict = true;
break;
}
}
if (!hasConflict) {
System.out.println(" Available: " + candidate + " - " + candidate.plus(meetingLength) + " (New York)");
break;
}
candidate = candidate.plusMinutes(30);
}
// ---- Date Calculations ----
System.out.println("\n--- Date Calculations ---");
// Recurring weekly event -- next 4 occurrences
LocalDate nextMonday = meetingDate.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY));
System.out.println("Next 4 Monday standups:");
for (int i = 0; i < 4; i++) {
LocalDate occurrence = nextMonday.plusWeeks(i);
System.out.println(" " + occurrence + " (" + occurrence.getDayOfWeek() + ")");
}
// Business days until deadline
LocalDate deadline = LocalDate.of(2024, 4, 1);
long businessDays = 0;
LocalDate check = meetingDate;
while (check.isBefore(deadline)) {
DayOfWeek dow = check.getDayOfWeek();
if (dow != DayOfWeek.SATURDAY && dow != DayOfWeek.SUNDAY) {
businessDays++;
}
check = check.plusDays(1);
}
System.out.println("\nBusiness days until " + deadline + ": " + businessDays);
// Quarter end date
int currentQuarter = (meetingDate.getMonthValue() - 1) / 3 + 1;
LocalDate quarterEnd = LocalDate.of(meetingDate.getYear(), currentQuarter * 3, 1)
.with(TemporalAdjusters.lastDayOfMonth());
Period untilQuarterEnd = Period.between(meetingDate, quarterEnd);
System.out.println("Quarter " + currentQuarter + " ends: " + quarterEnd
+ " (" + untilQuarterEnd.getMonths() + " months, "
+ untilQuarterEnd.getDays() + " days away)");
// ---- Age and Anniversary ----
System.out.println("\n--- Age Calculator ---");
LocalDate birthday = LocalDate.of(1990, Month.JULY, 20);
LocalDate today = LocalDate.of(2024, 3, 15);
Period age = Period.between(birthday, today);
System.out.println("Birthday: " + birthday);
System.out.println("Age: " + age.getYears() + " years, "
+ age.getMonths() + " months, " + age.getDays() + " days");
long totalDaysLived = ChronoUnit.DAYS.between(birthday, today);
System.out.println("Total days lived: " + String.format("%,d", totalDaysLived));
// Next birthday
LocalDate nextBirthday = birthday.withYear(today.getYear());
if (nextBirthday.isBefore(today) || nextBirthday.isEqual(today)) {
nextBirthday = nextBirthday.plusYears(1);
}
long daysUntilBirthday = ChronoUnit.DAYS.between(today, nextBirthday);
System.out.println("Next birthday: " + nextBirthday
+ " (" + daysUntilBirthday + " days away)");
// ---- Summary ----
System.out.println("\n=== Classes Used ===");
System.out.println("LocalDate - date without time");
System.out.println("LocalTime - time without date");
System.out.println("LocalDateTime - date and time without zone");
System.out.println("ZonedDateTime - date, time, and zone");
System.out.println("Instant - machine timestamp (UTC)");
System.out.println("Period - date-based amount (years/months/days)");
System.out.println("Duration - time-based amount (hours/minutes/seconds)");
System.out.println("ZoneId - time zone identifier");
System.out.println("DateTimeFormatter - formatting and parsing");
System.out.println("TemporalAdjusters - date adjustments (first/last day, next Monday)");
System.out.println("ChronoUnit - unit-based calculations");
}
}
// Sample Output:
// === Event Scheduling System ===
//
// --- Creating Events ---
//
// --- Events in Each Time Zone ---
//
// America/New_York:
// Team Standup: Mon, Mar 18 2024 at 9:00 AM EDT (0h 30m)
// Client Demo: Mon, Mar 18 2024 at 10:00 AM EDT (1h 30m)
// Sprint Planning: Sun, Mar 17 2024 at 9:00 PM EDT (2h 0m)
//
// Europe/London:
// Team Standup: Mon, Mar 18 2024 at 1:00 PM GMT (0h 30m)
// Client Demo: Mon, Mar 18 2024 at 2:00 PM GMT (1h 30m)
// Sprint Planning: Mon, Mar 18 2024 at 1:00 AM GMT (2h 0m)
//
// Asia/Tokyo:
// Team Standup: Mon, Mar 18 2024 at 10:00 PM JST (0h 30m)
// Client Demo: Mon, Mar 18 2024 at 11:00 PM JST (1h 30m)
// Sprint Planning: Mon, Mar 18 2024 at 10:00 AM JST (2h 0m)
//
// Asia/Kolkata:
// Team Standup: Mon, Mar 18 2024 at 6:30 PM IST (0h 30m)
// Client Demo: Mon, Mar 18 2024 at 7:30 PM IST (1h 30m)
// Sprint Planning: Mon, Mar 18 2024 at 6:30 AM IST (2h 0m)
//
// --- Conflict Detection ---
// CONFLICT: Team Standup overlaps with Client Demo
// OK: Team Standup and Sprint Planning do not overlap
// OK: Client Demo and Sprint Planning do not overlap
//
// --- Business days until 2024-04-01: 10
// --- Age: 33 years, 7 months, 24 days
// === Classes Used ===
// LocalDate, LocalTime, ZonedDateTime, Duration, Period, ZoneId,
// DateTimeFormatter, TemporalAdjusters, ChronoUnit
| Task | Code |
|---|---|
| Current date | LocalDate.now() |
| Current time | LocalTime.now() |
| Current date-time | LocalDateTime.now() |
| Current instant | Instant.now() |
| Specific date | LocalDate.of(2024, Month.MARCH, 15) |
| Parse date string | LocalDate.parse("2024-03-15") |
| Format date | date.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")) |
| Add days | date.plusDays(5) |
| Subtract months | date.minusMonths(3) |
| Days between dates | ChronoUnit.DAYS.between(date1, date2) |
| Period between dates | Period.between(date1, date2) |
| Duration between times | Duration.between(time1, time2) |
| Compare dates | date1.isBefore(date2), isAfter(), isEqual() |
| First day of month | date.with(TemporalAdjusters.firstDayOfMonth()) |
| Next Monday | date.with(TemporalAdjusters.next(DayOfWeek.MONDAY)) |
| Convert time zone | zdt.withZoneSameInstant(ZoneId.of("Asia/Tokyo")) |
| Date to Instant | oldDate.toInstant() |
| Instant to Date | Date.from(instant) |
| Leap year check | date.isLeapYear() |
| Day of week | date.getDayOfWeek() |