Date and Time API in Java

1. Problems with the Old Date API

Before Java 8, date and time handling relied on java.util.Date and java.util.Calendar. These classes had severe design problems that caused bugs and confusion for years:

  • Mutable and not thread-safeDate objects can be changed after creation, causing bugs in shared or cached state.
  • Poor API design — months in Calendar are 0-indexed (January = 0, December = 11), years in Date are relative to 1900, and years in Calendar are used normally — two inconsistent conventions in the same API.
  • No separation of conceptsDate represents a point in time (timestamp) but is also used as a calendar date, with no clean way to represent just a date, just a time, or a date+time without a timezone.
  • Confusing timezone handling — mixing of local and UTC times leads to subtle off-by-hours bugs.
  • Formatting is not thread-safeSimpleDateFormat is stateful and must not be shared across threads.
// Old API — full of traps
import java.util.*;

public class OldDateProblems {
    public static void main(String[] args) {
        // java.util.Date — year is offset from 1900, month is 0-indexed!
        Date d = new Date(2024 - 1900, 0, 15);  // Jan 15, 2024
        System.out.println("Old Date: " + d);   // Confusing output

        // java.util.Calendar — months still 0-indexed
        Calendar cal = Calendar.getInstance();
        cal.set(2024, Calendar.JANUARY, 15);  // Must use constant or risk off-by-one
        System.out.println("Year: "  + cal.get(Calendar.YEAR));
        System.out.println("Month: " + cal.get(Calendar.MONTH)); // 0 = January!
        System.out.println("Day: "   + cal.get(Calendar.DAY_OF_MONTH));

        // Mutable — dangerous when shared
        Date original = new Date();
        Date copy = original;  // not a copy — same reference
        copy.setTime(0);        // accidentally mutates 'original' too
        System.out.println("original == copy? " + (original.getTime() == copy.getTime()));
    }
}

Best practice: Avoid java.util.Date and java.util.Calendar in new code. Use java.time classes introduced in Java 8 instead.

2. Overview of java.time Package

The java.time package (inspired by the Joda-Time library) provides a clean, immutable, and thread-safe date/time API. All objects are immutable — operations return new instances rather than modifying existing ones.

Class Represents Example
LocalDate Date without time or timezone 2026-07-23
LocalTime Time without date or timezone 14:30:00.000
LocalDateTime Date + time without timezone 2026-07-23T14:30:00
ZonedDateTime Date + time + timezone 2026-07-23T14:30:00-05:00[America/New_York]
Instant Machine timestamp (epoch seconds) 2026-07-23T19:30:00Z
Duration Time-based amount (hours, minutes, seconds) PT2H30M
Period Date-based amount (years, months, days) P1Y6M10D
DateTimeFormatter Format/parse date-time strings "dd-MM-yyyy HH:mm"

Immutability: All java.time classes are immutable and thread-safe. Methods like plusDays() return a new object — the original is unchanged.

3. LocalDate

LocalDate represents a date (year, month, day) without any time-of-day or timezone information. It is ideal for representing birthdays, anniversaries, deadlines, or any concept that is just a calendar date.

import java.time.*;
import java.time.temporal.ChronoUnit;

public class LocalDateDemo {
    public static void main(String[] args) {
        // Create LocalDate instances
        LocalDate today   = LocalDate.now();             // current date
        LocalDate specific = LocalDate.of(2026, 7, 23); // July 23, 2026
        LocalDate parsed  = LocalDate.parse("2025-12-25"); // ISO-8601 string

        System.out.println("Today:    " + today);
        System.out.println("Specific: " + specific);
        System.out.println("Parsed:   " + parsed);

        // Accessing fields
        System.out.println("Year:        " + today.getYear());
        System.out.println("Month:       " + today.getMonth());       // JULY
        System.out.println("MonthValue:  " + today.getMonthValue());  // 7 (1-indexed!)
        System.out.println("Day:         " + today.getDayOfMonth());
        System.out.println("DayOfWeek:   " + today.getDayOfWeek());   // WEDNESDAY
        System.out.println("DayOfYear:   " + today.getDayOfYear());
        System.out.println("IsLeapYear:  " + today.isLeapYear());

        // Arithmetic — returns new instances (immutable)
        LocalDate nextWeek      = today.plusDays(7);
        LocalDate prevMonth     = today.minusMonths(1);
        LocalDate nextYear      = today.plusYears(1);
        System.out.println("Next week:   " + nextWeek);
        System.out.println("Prev month:  " + prevMonth);
        System.out.println("Next year:   " + nextYear);

        // Comparison
        LocalDate date1 = LocalDate.of(2024, 1, 1);
        LocalDate date2 = LocalDate.of(2025, 6, 15);
        System.out.println("date1 isBefore date2? " + date1.isBefore(date2));
        System.out.println("date2 isAfter  date1? " + date2.isAfter(date1));
        System.out.println("Same date?            " + date1.isEqual(date1));

        // Days between two dates
        long daysBetween = ChronoUnit.DAYS.between(date1, date2);
        System.out.println("Days between: " + daysBetween);
    }
}
Today: 2026-07-23
Specific: 2026-07-23
Parsed: 2025-12-25
Year: 2026
Month: JULY
MonthValue: 7
Day: 23
DayOfWeek: THURSDAY
DayOfYear: 204
IsLeapYear: false
Next week: 2026-07-30
Prev month: 2026-06-23
Next year: 2027-07-23
date1 isBefore date2? true
date2 isAfter date1? true
Same date? true
Days between: 531

4. LocalTime

LocalTime represents a time-of-day (hour, minute, second, nanosecond) without any date or timezone. It is useful for representing opening hours, alarm times, or any recurring daily time.

import java.time.*;

public class LocalTimeDemo {
    public static void main(String[] args) {
        // Create LocalTime instances
        LocalTime now     = LocalTime.now();                    // current time
        LocalTime noon    = LocalTime.of(12, 0);               // 12:00:00
        LocalTime precise = LocalTime.of(14, 30, 45, 500000000); // 14:30:45.5
        LocalTime parsed  = LocalTime.parse("08:15:30");

        System.out.println("Now:     " + now);
        System.out.println("Noon:    " + noon);
        System.out.println("Precise: " + precise);
        System.out.println("Parsed:  " + parsed);

        // Accessing fields
        System.out.println("Hour:   " + now.getHour());
        System.out.println("Minute: " + now.getMinute());
        System.out.println("Second: " + now.getSecond());
        System.out.println("Nano:   " + now.getNano());

        // Arithmetic
        LocalTime twoHoursLater    = noon.plusHours(2);
        LocalTime thirtyMinEarlier = noon.minusMinutes(30);
        System.out.println("Noon + 2h:    " + twoHoursLater);
        System.out.println("Noon - 30min: " + thirtyMinEarlier);

        // Comparison
        System.out.println("noon isBefore precise? " + noon.isBefore(precise));
        System.out.println("noon isAfter  parsed?  " + noon.isAfter(parsed));

        // Truncation — useful for ignoring sub-second precision
        LocalTime truncated = now.withNano(0).withSecond(0); // floor to minute
        System.out.println("Truncated to minute: " + truncated);

        // Constants
        System.out.println("Midnight: " + LocalTime.MIDNIGHT); // 00:00
        System.out.println("Noon:     " + LocalTime.NOON);     // 12:00
    }
}
Now: 14:30:22.847
Noon: 12:00
Precise: 14:30:45.500
Parsed: 08:15:30
Hour: 14
Minute: 30
Second: 22
Nano: 847000000
Noon + 2h: 14:00
Noon - 30min: 11:30
noon isBefore precise? true
noon isAfter parsed? true
Truncated to minute: 14:30
Midnight: 00:00
Noon: 12:00

5. LocalDateTime

LocalDateTime combines a date and a time in a single object, without any timezone information. It is the most commonly used class when you need to store both date and time but do not need to account for timezones (e.g., a log entry timestamp stored in a database alongside timezone metadata, or a UI display value).

import java.time.*;
import java.time.format.DateTimeFormatter;

public class LocalDateTimeDemo {
    public static void main(String[] args) {
        // Create LocalDateTime instances
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime specific = LocalDateTime.of(2026, 7, 23, 14, 30, 0);
        LocalDateTime fromParts = LocalDateTime.of(
            LocalDate.of(2026, 1, 1),
            LocalTime.of(9, 0)
        );

        System.out.println("Now:       " + now);
        System.out.println("Specific:  " + specific);
        System.out.println("FromParts: " + fromParts);

        // Arithmetic
        LocalDateTime meeting = specific.plusDays(3).plusHours(2).minusMinutes(15);
        System.out.println("Meeting:   " + meeting);

        // Extract date/time parts
        System.out.println("Date part: " + specific.toLocalDate());
        System.out.println("Time part: " + specific.toLocalTime());
        System.out.println("Year:      " + specific.getYear());
        System.out.println("DayOfWeek: " + specific.getDayOfWeek());

        // Formatting
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm");
        String formatted = specific.format(fmt);
        System.out.println("Formatted: " + formatted);

        // Parsing
        LocalDateTime parsed = LocalDateTime.parse("23-07-2026 14:30", fmt);
        System.out.println("Parsed:    " + parsed);

        // isBefore / isAfter
        System.out.println("specific isBefore meeting? " + specific.isBefore(meeting));
    }
}
Now: 2026-07-23T14:30:22.847
Specific: 2026-07-23T14:30
FromParts: 2026-01-01T09:00
Meeting: 2026-07-26T16:15
Date part: 2026-07-23
Time part: 14:30
Year: 2026
DayOfWeek: THURSDAY
Formatted: 23-07-2026 14:30
Parsed: 2026-07-23T14:30
specific isBefore meeting? true

6. ZonedDateTime

ZonedDateTime is a LocalDateTime with full timezone information. Use it when scheduling across timezones, displaying times to users in different regions, or converting between timezones. Timezones are identified by ZoneId strings like "America/New_York" or "Asia/Kolkata".

import java.time.*;
import java.time.format.DateTimeFormatter;

public class ZonedDateTimeDemo {
    public static void main(String[] args) {
        // Current time in different timezones
        ZonedDateTime utc        = ZonedDateTime.now(ZoneId.of("UTC"));
        ZonedDateTime newYork    = ZonedDateTime.now(ZoneId.of("America/New_York"));
        ZonedDateTime london     = ZonedDateTime.now(ZoneId.of("Europe/London"));
        ZonedDateTime kolkata    = ZonedDateTime.now(ZoneId.of("Asia/Kolkata"));
        ZonedDateTime tokyo      = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));

        System.out.println("UTC:      " + utc);
        System.out.println("New York: " + newYork);
        System.out.println("London:   " + london);
        System.out.println("Kolkata:  " + kolkata);
        System.out.println("Tokyo:    " + tokyo);

        // Create a specific ZonedDateTime
        ZonedDateTime meeting = ZonedDateTime.of(
            LocalDateTime.of(2026, 7, 23, 9, 0),
            ZoneId.of("America/New_York")
        );
        System.out.println("\nMeeting (NY):     " + meeting);

        // Convert to another timezone
        ZonedDateTime meetingLondon = meeting.withZoneSameInstant(ZoneId.of("Europe/London"));
        ZonedDateTime meetingKolkata = meeting.withZoneSameInstant(ZoneId.of("Asia/Kolkata"));
        System.out.println("Meeting (London):  " + meetingLondon);
        System.out.println("Meeting (Kolkata): " + meetingKolkata);

        // List all available zone IDs (first 5)
        System.out.println("\nSample Zone IDs:");
        ZoneId.getAvailableZoneIds().stream()
              .sorted()
              .limit(5)
              .forEach(z -> System.out.println("  " + z));

        // Formatting ZonedDateTime
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm z");
        System.out.println("\nFormatted: " + meeting.format(fmt));
    }
}
UTC: 2026-07-23T14:30:22.847Z[UTC]
New York: 2026-07-23T10:30:22.847-04:00[America/New_York]
London: 2026-07-23T15:30:22.847+01:00[Europe/London]
Kolkata: 2026-07-23T20:00:22.847+05:30[Asia/Kolkata]
Tokyo: 2026-07-23T23:30:22.847+09:00[Asia/Tokyo]

Meeting (NY): 2026-07-23T09:00-04:00[America/New_York]
Meeting (London): 2026-07-23T14:00+01:00[Europe/London]
Meeting (Kolkata): 2026-07-23T18:30+05:30[Asia/Kolkata]

Sample Zone IDs:
Africa/Abidjan
Africa/Accra
Africa/Addis_Ababa
Africa/Algiers
Africa/Asmara

Formatted: 23 Jul 2026 09:00 EDT

7. Instant

Instant represents a single point on the timeline — a machine timestamp measured as the number of seconds (and nanoseconds) since the Unix epoch (January 1, 1970, 00:00:00 UTC). It has no concept of timezone, date, or time-of-day. Use it for measuring elapsed time, storing event timestamps in databases, and interoperating with systems that use epoch times.

import java.time.*;

public class InstantDemo {
    public static void main(String[] args) {
        // Current moment
        Instant now = Instant.now();
        System.out.println("Now (ISO):        " + now);
        System.out.println("Epoch seconds:    " + now.getEpochSecond());
        System.out.println("Epoch millis:     " + now.toEpochMilli());
        System.out.println("Nano adjustment:  " + now.getNano());

        // Create from epoch value
        Instant fromEpoch = Instant.ofEpochSecond(0);        // Unix epoch
        Instant fromMillis = Instant.ofEpochMilli(1_000_000_000_000L); // 2001
        System.out.println("Unix epoch:       " + fromEpoch);
        System.out.println("From millis:      " + fromMillis);

        // Arithmetic
        Instant future = now.plusSeconds(3600);  // 1 hour later
        Instant past   = now.minusSeconds(86400); // 1 day ago
        System.out.println("1 hour later:     " + future);
        System.out.println("1 day ago:        " + past);

        // Comparison
        System.out.println("past isBefore now? " + past.isBefore(now));
        System.out.println("future isAfter now? " + future.isAfter(now));

        // Convert Instant to ZonedDateTime (for display)
        ZonedDateTime zdt = now.atZone(ZoneId.of("America/New_York"));
        System.out.println("Instant as ZDT:   " + zdt);

        // Measure elapsed time
        Instant start = Instant.now();
        long sum = 0;
        for (int i = 0; i < 1_000_000; i++) sum += i;
        Instant end = Instant.now();
        long elapsedMs = end.toEpochMilli() - start.toEpochMilli();
        System.out.println("Sum: " + sum + ", elapsed: " + elapsedMs + "ms");
    }
}
Now (ISO): 2026-07-23T14:30:22.847Z
Epoch seconds: 1753281022
Epoch millis: 1753281022847
Nano adjustment: 847000000
Unix epoch: 1970-01-01T00:00:00Z
From millis: 2001-09-08T21:46:40Z
1 hour later: 2026-07-23T15:30:22.847Z
1 day ago: 2026-07-22T14:30:22.847Z
past isBefore now? true
future isAfter now? true
Instant as ZDT: 2026-07-23T10:30:22.847-04:00[America/New_York]
Sum: 499999500000, elapsed: 3ms

8. Duration

Duration represents a time-based amount — a quantity of time measured in hours, minutes, seconds, and nanoseconds. It is best suited for measuring differences between Instant or LocalTime values (precise time spans).

import java.time.*;

public class DurationDemo {
    public static void main(String[] args) {
        // Create Duration instances
        Duration twoHours        = Duration.ofHours(2);
        Duration thirtyMinutes   = Duration.ofMinutes(30);
        Duration ninetySeconds   = Duration.ofSeconds(90);
        Duration mixed           = Duration.ofHours(1).plusMinutes(30).plusSeconds(45);

        System.out.println("2 hours:         " + twoHours);        // PT2H
        System.out.println("30 minutes:      " + thirtyMinutes);   // PT30M
        System.out.println("90 seconds:      " + ninetySeconds);   // PT1M30S
        System.out.println("1h 30m 45s:      " + mixed);           // PT1H30M45S

        // Duration between two Instants
        Instant start = Instant.parse("2026-07-23T08:00:00Z");
        Instant end   = Instant.parse("2026-07-23T10:45:30Z");
        Duration between = Duration.between(start, end);
        System.out.println("Between instants: " + between);         // PT2H45M30S
        System.out.println("  toHours:   " + between.toHours());
        System.out.println("  toMinutes: " + between.toMinutes());
        System.out.println("  toSeconds: " + between.toSeconds());
        System.out.println("  toMillis:  " + between.toMillis());

        // Duration between two LocalTimes
        LocalTime open  = LocalTime.of(9, 0);
        LocalTime close = LocalTime.of(17, 30);
        Duration workDay = Duration.between(open, close);
        System.out.println("Work day: " + workDay);                 // PT8H30M
        System.out.printf("Work day: %d hours %d minutes%n",
            workDay.toHoursPart(), workDay.toMinutesPart());

        // Arithmetic
        Duration doubled = twoHours.multipliedBy(2);
        Duration halved  = twoHours.dividedBy(2);
        System.out.println("Doubled: " + doubled);
        System.out.println("Halved:  " + halved);

        // Negative duration
        Duration negative = Duration.between(end, start);
        System.out.println("Negative: " + negative);               // PT-2H-45M-30S
        System.out.println("isNegative? " + negative.isNegative());
        System.out.println("abs: " + negative.abs());
    }
}
2 hours: PT2H
30 minutes: PT30M
90 seconds: PT1M30S
1h 30m 45s: PT1H30M45S
Between instants: PT2H45M30S
toHours: 2
toMinutes: 165
toSeconds: 9930
toMillis: 9930000
Work day: PT8H30M
Work day: 8 hours 30 minutes
Doubled: PT4H
Halved: PT1H
Negative: PT-2H-45M-30S
isNegative? true
abs: PT2H45M30S

9. Period

Period represents a date-based amount in years, months, and days. It operates on calendar dates — unlike Duration which operates on exact time. Use Period for human-meaningful date spans like age calculation, subscription length, or contract durations.

import java.time.*;

public class PeriodDemo {
    public static void main(String[] args) {
        // Create Period instances
        Period oneYear       = Period.ofYears(1);
        Period sixMonths     = Period.ofMonths(6);
        Period tenDays       = Period.ofDays(10);
        Period oneYearFive   = Period.of(1, 5, 10); // 1 year, 5 months, 10 days

        System.out.println("1 year:           " + oneYear);     // P1Y
        System.out.println("6 months:         " + sixMonths);   // P6M
        System.out.println("10 days:          " + tenDays);     // P10D
        System.out.println("1Y 5M 10D:        " + oneYearFive); // P1Y5M10D

        // Period between two LocalDates
        LocalDate birth  = LocalDate.of(2000, 3, 15);
        LocalDate today  = LocalDate.of(2026, 7, 23);
        Period age = Period.between(birth, today);
        System.out.println("Age period:       " + age);
        System.out.printf("Age: %d years, %d months, %d days%n",
            age.getYears(), age.getMonths(), age.getDays());

        // Add Period to a date
        LocalDate start     = LocalDate.of(2026, 1, 1);
        LocalDate afterYear = start.plus(Period.ofYears(1));
        LocalDate afterSix  = start.plus(Period.ofMonths(6));
        System.out.println("After 1 year:  " + afterYear);
        System.out.println("After 6 months: " + afterSix);

        // Period arithmetic
        Period doubled = Period.of(age.getYears() * 2,
                                   age.getMonths() * 2,
                                   age.getDays() * 2);
        System.out.println("Doubled period: " + doubled);

        // isNegative
        Period neg = Period.between(today, birth);
        System.out.println("Negative: " + neg.isNegative());

        // Practical: check if 18+ years old
        LocalDate dob = LocalDate.of(2010, 5, 20);
        Period sinceB = Period.between(dob, today);
        boolean isAdult = sinceB.getYears() >= 18;
        System.out.println("Born " + dob + " — adult? " + isAdult);
    }
}
1 year: P1Y
6 months: P6M
10 days: P10D
1Y 5M 10D: P1Y5M10D
Age period: P26Y4M8D
Age: 26 years, 4 months, 8 days
After 1 year: 2027-01-01
After 6 months: 2026-07-01
Doubled period: P52Y8M16D
Negative: true
Born 2010-05-20 — adult? false

Duration vs Period: Use Duration for machine-level, exact time spans (seconds, nanoseconds). Use Period for human calendar spans (years, months, days). Do not mix them — a month is not a fixed number of seconds.

10. DateTimeFormatter

DateTimeFormatter is the thread-safe replacement for SimpleDateFormat. It can format date/time objects to strings and parse strings into date/time objects. It offers predefined formatters and custom pattern-based formatters.

import java.time.*;
import java.time.format.*;
import java.util.Locale;

public class DateTimeFormatterDemo {
    public static void main(String[] args) {
        LocalDateTime ldt = LocalDateTime.of(2026, 7, 23, 14, 30, 45);
        LocalDate     ld  = ldt.toLocalDate();
        ZonedDateTime zdt = ldt.atZone(ZoneId.of("America/New_York"));

        // --- Predefined Formatters ---
        System.out.println("ISO_DATE:            " +
            ld.format(DateTimeFormatter.ISO_DATE));
        System.out.println("ISO_DATE_TIME:       " +
            ldt.format(DateTimeFormatter.ISO_DATE_TIME));
        System.out.println("ISO_ZONED_DATE_TIME: " +
            zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
        System.out.println("RFC_1123_DATE_TIME:  " +
            zdt.format(DateTimeFormatter.RFC_1123_DATE_TIME));
        System.out.println("ISO_LOCAL_DATE:      " +
            ld.format(DateTimeFormatter.ISO_LOCAL_DATE));

        // --- Custom Patterns ---
        DateTimeFormatter fmt1 = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm");
        DateTimeFormatter fmt2 = DateTimeFormatter.ofPattern("MMMM dd, yyyy");
        DateTimeFormatter fmt3 = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss");
        DateTimeFormatter fmt4 = DateTimeFormatter.ofPattern("yyyy/MM/dd h:mm a");
        DateTimeFormatter fmt5 = DateTimeFormatter.ofPattern("dd MMM yyyy", Locale.FRENCH);

        System.out.println("\nCustom dd-MM-yyyy HH:mm:  " + ldt.format(fmt1));
        System.out.println("Custom MMMM dd, yyyy:      " + ld.format(fmt2));
        System.out.println("Custom EEE, dd MMM HH:mm:  " + ldt.format(fmt3));
        System.out.println("Custom yyyy/MM/dd h:mm a:  " + ldt.format(fmt4));
        System.out.println("Custom French locale:       " + ld.format(fmt5));

        // --- Parsing strings to date/time objects ---
        System.out.println("\n--- Parsing ---");
        String s1 = "23-07-2026 14:30";
        LocalDateTime parsed1 = LocalDateTime.parse(s1, fmt1);
        System.out.println("Parsed LocalDateTime: " + parsed1);

        String s2 = "2026-07-23";
        LocalDate parsed2 = LocalDate.parse(s2); // ISO-8601 default
        System.out.println("Parsed LocalDate:     " + parsed2);

        String s3 = "July 23, 2026";
        DateTimeFormatter fmtParse = DateTimeFormatter.ofPattern("MMMM dd, yyyy");
        LocalDate parsed3 = LocalDate.parse(s3, fmtParse);
        System.out.println("Parsed 'July 23, 2026': " + parsed3);

        // --- Format pattern reference ---
        System.out.println("\n--- Pattern Reference ---");
        DateTimeFormatter[] patterns = {
            DateTimeFormatter.ofPattern("yyyy"),
            DateTimeFormatter.ofPattern("MM"),
            DateTimeFormatter.ofPattern("MMMM"),
            DateTimeFormatter.ofPattern("dd"),
            DateTimeFormatter.ofPattern("EEE"),
            DateTimeFormatter.ofPattern("EEEE"),
            DateTimeFormatter.ofPattern("HH"),
            DateTimeFormatter.ofPattern("mm"),
            DateTimeFormatter.ofPattern("ss"),
            DateTimeFormatter.ofPattern("SSS")
        };
        String[] meanings = {
            "yyyy = year (4-digit)",
            "MM   = month (2-digit)",
            "MMMM = month (full name)",
            "dd   = day (2-digit)",
            "EEE  = weekday (abbrev)",
            "EEEE = weekday (full)",
            "HH   = hour 24h",
            "mm   = minute",
            "ss   = second",
            "SSS  = milliseconds"
        };
        for (int i = 0; i < patterns.length; i++) {
            System.out.println(meanings[i] + " -> " + ldt.format(patterns[i]));
        }
    }
}
ISO_DATE: 2026-07-23
ISO_DATE_TIME: 2026-07-23T14:30:45
ISO_ZONED_DATE_TIME: 2026-07-23T14:30:45-04:00[America/New_York]
RFC_1123_DATE_TIME: Thu, 23 Jul 2026 14:30:45 -0400
ISO_LOCAL_DATE: 2026-07-23

Custom dd-MM-yyyy HH:mm: 23-07-2026 14:30
Custom MMMM dd, yyyy: July 23, 2026
Custom EEE, dd MMM HH:mm: Thu, 23 Jul 14:30:45
Custom yyyy/MM/dd h:mm a: 2026/07/23 2:30 PM
Custom French locale: 23 juil. 2026

--- Parsing ---
Parsed LocalDateTime: 2026-07-23T14:30
Parsed LocalDate: 2026-07-23
Parsed 'July 23, 2026': 2026-07-23

--- Pattern Reference ---
yyyy = year (4-digit) -> 2026
MM = month (2-digit) -> 07
MMMM = month (full name) -> July
dd = day (2-digit) -> 23
EEE = weekday (abbrev) -> Thu
EEEE = weekday (full) -> Thursday
HH = hour 24h -> 14
mm = minute -> 30
ss = second -> 45
SSS = milliseconds -> 000

11. Comparison: Old vs New API

Here is a comprehensive side-by-side comparison of the old and new APIs:

Concept Old API (java.util) New API (java.time)
Current date new Date() LocalDate.now()
Current date and time new Date() or Calendar.getInstance() LocalDateTime.now()
Specific date new Date(year-1900, month-1, day) LocalDate.of(year, month, day)
Date with timezone Calendar.getInstance(TimeZone.getTimeZone("...")) ZonedDateTime.now(ZoneId.of("..."))
Machine timestamp System.currentTimeMillis() Instant.now()
Add days cal.add(Calendar.DAY_OF_MONTH, n) date.plusDays(n)
Subtract months cal.add(Calendar.MONTH, -n) date.minusMonths(n)
Days between dates Manual subtraction of milliseconds ChronoUnit.DAYS.between(d1, d2)
Date-based span No equivalent Period.between(d1, d2)
Time-based span Millisecond arithmetic Duration.between(t1, t2)
Format date new SimpleDateFormat("...").format(date) DateTimeFormatter.ofPattern("...").format(ldt)
Parse string to date new SimpleDateFormat("...").parse(str) LocalDate.parse(str, formatter)
Thread safety Not thread-safe (SimpleDateFormat) Fully thread-safe (immutable)
Mutability Mutable (setTime, set, etc.) Immutable (returns new objects)
Month indexing 0-indexed (Jan = 0) 1-indexed (Jan = 1) — intuitive

Continue learning