LocalDate

Su Suriya Ravichandran Updated 14 Nov 2026
5 min read ·Lesson 114 of 133

java.time.LocalDate represents a calendar date (year, month, and day) with no time of day and no time zone. It is the right type for birthdays, due dates, holidays, invoice dates, course start dates, and any value people think of as "a day" rather than "a moment." LocalDate is immutable, thread-safe, and follows the ISO calendar used in most of the world.

Creating a LocalDate

import java.time.*;

LocalDate today = LocalDate.now();                         // current date in the default zone
LocalDate inZone = LocalDate.now(ZoneId.of("Asia/Kolkata")); // "today" in a specific zone
LocalDate d = LocalDate.of(2026, 11, 12);                  // year, month (1-12), day
LocalDate d2 = LocalDate.of(2026, Month.NOVEMBER, 12);     // month as an enum
LocalDate parsed = LocalDate.parse("2026-11-12");          // ISO-8601 text
LocalDate dayOfYear = LocalDate.ofYearDay(2026, 256);      // 2026-09-13
LocalDate epoch = LocalDate.ofEpochDay(0);                 // 1970-01-01

Months are numbered 1 to 12 (unlike the legacy Calendar, where January was 0). Invalid dates such as February 30 throw DateTimeException when created.

"Today" depends on the time zone: when it is 11 pm on the 12th in New York, it is already the 13th in Chennai. Server code that needs "today" for a particular user should pass that user's ZoneId to now.

Reading Fields

LocalDate d = LocalDate.of(2026, 11, 12);

d.getYear();           // 2026
d.getMonth();          // NOVEMBER (a Month enum)
d.getMonthValue();     // 11
d.getDayOfMonth();     // 12
d.getDayOfWeek();      // THURSDAY (a DayOfWeek enum)
d.getDayOfYear();      // 316
d.lengthOfMonth();     // 30
d.lengthOfYear();      // 365
d.isLeapYear();        // false

Month and DayOfWeek are enums, so they can be compared with ==, used in switch, and stored in EnumSet/EnumMap.

Adding and Subtracting

d.plusDays(10);        // 2026-11-22
d.plusWeeks(2);        // 2026-11-26
d.plusMonths(3);       // 2027-02-12
d.minusYears(1);       // 2025-11-12
d.plus(Period.ofMonths(6));

Results are always valid dates. When the target month is shorter, the day is clamped to that month's last day:

LocalDate.of(2026, 3, 31).minusMonths(1);    // 2026-02-28
LocalDate.of(2024, 2, 29).plusYears(1);      // 2025-02-28

This is usually what people expect, but it means adding a month and then subtracting a month does not always return the original date. Repeatedly adding one month to January 31 drifts to the 28th and stays there; compute each date from the original start instead (start.plusMonths(n)).

Replacing Fields and Using Adjusters

with... methods return a copy with one field changed:

d.withDayOfMonth(1);        // 2026-11-01
d.withMonth(2);             // 2026-02-12
d.withYear(2030);           // 2030-11-12

TemporalAdjusters answer common calendar questions:

import java.time.temporal.TemporalAdjusters;

d.with(TemporalAdjusters.firstDayOfMonth());               // 2026-11-01
d.with(TemporalAdjusters.lastDayOfMonth());                // 2026-11-30
d.with(TemporalAdjusters.next(DayOfWeek.MONDAY));          // 2026-11-16
d.with(TemporalAdjusters.nextOrSame(DayOfWeek.THURSDAY));  // 2026-11-12
d.with(TemporalAdjusters.firstInMonth(DayOfWeek.FRIDAY));  // 2026-11-06
d.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY));   // 2026-11-27
d.with(TemporalAdjusters.firstDayOfNextMonth());           // 2026-12-01

These replace the error-prone manual arithmetic that billing, payroll, and scheduling code otherwise needs.

Comparing Dates

LocalDate due = LocalDate.of(2026, 12, 1);

d.isBefore(due);        // true
d.isAfter(due);         // false
d.isEqual(due);         // false
d.equals(due);          // false
d.compareTo(due) < 0;   // true

LocalDate implements Comparable, so lists of dates can be sorted directly and dates can be used as TreeMap keys for date-ordered data.

Measuring the Gap Between Dates

import java.time.temporal.ChronoUnit;

LocalDate newYear = LocalDate.of(2027, 1, 1);

d.until(newYear);                         // P1M20D: 1 month and 20 days
d.until(newYear, ChronoUnit.DAYS);        // 50
ChronoUnit.DAYS.between(d, newYear);      // 50
ChronoUnit.WEEKS.between(d, newYear);     // 7
Period.between(LocalDate.of(1998, 2, 14), d).getYears();   // 28: age in full years

The end date is exclusive: from the 12th to the 13th is one day. The result is negative if the end is before the start.

Ranges of Dates

datesUntil (Java 9+) produces a stream of consecutive dates, end exclusive:

d.datesUntil(d.plusDays(5))
 .map(x -> x.getDayOfWeek())
 .toList();                     // [THURSDAY, FRIDAY, SATURDAY, SUNDAY, MONDAY]

d.datesUntil(d.plusMonths(1), Period.ofWeeks(1)).toList();   // every 7 days

This makes it easy to build calendars, generate reports per day, or find dates matching a rule.

ClassRepresentsExample
YearMonthA month of a year, without a dayCard expiry 2028-07, monthly reports
MonthDayA day of a month, without a yearAnnual events: birthdays, festivals
YearA yearYear.of(2028).isLeap() → true
YearMonth.of(2026, 2).lengthOfMonth();      // 28
MonthDay.of(2, 14).atYear(2027);            // 2027-02-14

Using these types documents intent: a credit-card expiry is genuinely a YearMonth, not a date with an arbitrary day.

Converting LocalDate

d.atTime(9, 30);                              // LocalDateTime 2026-11-12T09:30
d.atStartOfDay(ZoneId.of("Asia/Kolkata"));    // ZonedDateTime at midnight in that zone
d.toEpochDay();                               // 20769 days since 1970-01-01
d.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));   // "12/11/2026"

Use atStartOfDay(zone) rather than atTime(0, 0) when converting to a moment: in a few time zones, a daylight-saving change means midnight does not exist on some dates, and atStartOfDay handles that correctly.

Practical Example: Business-Day Due Dates and Payroll

An invoicing system gives customers five business days to pay, skipping weekends and public holidays. Payroll runs on the last business day of each month.

import java.time.*;
import java.time.temporal.TemporalAdjusters;
import java.util.*;

public class LD {
    static final Set<LocalDate> HOLIDAYS = Set.of(LocalDate.of(2026, 11, 26), LocalDate.of(2026, 12, 25));

    static boolean isBusinessDay(LocalDate d) {
        DayOfWeek dow = d.getDayOfWeek();
        return dow != DayOfWeek.SATURDAY && dow != DayOfWeek.SUNDAY && !HOLIDAYS.contains(d);
    }

    static LocalDate addBusinessDays(LocalDate start, int days) {
        LocalDate d = start;
        int added = 0;
        while (added < days) {
            d = d.plusDays(1);
            if (isBusinessDay(d)) added++;
        }
        return d;
    }

    public static void main(String[] args) {
        LocalDate invoiceDate = LocalDate.of(2026, 11, 20);
        LocalDate due = addBusinessDays(invoiceDate, 5);
        LocalDate monthEnd = invoiceDate.with(TemporalAdjusters.lastDayOfMonth());
        System.out.println("Invoice " + invoiceDate + " (" + invoiceDate.getDayOfWeek() + ") due " + due + " (" + due.getDayOfWeek() + ")");
        System.out.println("Month-end close: " + monthEnd + ", business day? " + isBusinessDay(monthEnd));

        List<LocalDate> payroll = new ArrayList<>();
        for (int m = 1; m <= 12; m++) {
            LocalDate last = LocalDate.of(2026, m, 1).with(TemporalAdjusters.lastDayOfMonth());
            while (!isBusinessDay(last)) last = last.minusDays(1);
            payroll.add(last);
        }
        System.out.println(payroll.subList(9, 12));
    }
}

Output:

Invoice 2026-11-20 (FRIDAY) due 2026-11-30 (MONDAY)
Month-end close: 2026-11-30, business day? true
[2026-10-30, 2026-11-30, 2026-12-31]

isBusinessDay combines the DayOfWeek enum with a Set of holiday dates; because LocalDate implements equals and hashCode by value, HOLIDAYS.contains(d) works with any LocalDate object for the same day. addBusinessDays steps forward one day at a time and counts only business days: from Friday 20 November it counts Monday 23rd, Tuesday 24th, Wednesday 25th, skips the holiday on the 26th, counts Friday 27th, skips the weekend, and lands on Monday 30th. The payroll loop uses lastDayOfMonth and then steps backward past weekends: October 31, 2026 is a Saturday, so payroll moves to Friday the 30th. Because every operation returns a new LocalDate, the loops simply reassign d and last at each step.

0 Comments

Reviewed before they appear

No comments yet.

Java
Java Introduction Java Features Java Syntax Java Comments Java Statements Java Output Java Variables Java Data Types Java Type Casting Java Operators Java Strings Java Numbers Java Math Java Booleans Java User Input Java Conditions Java If Else Java Switch Java While Loop Java For Loop Java Break Java Continue Java Arrays Java Methods Java Parameters Java Return Values Java Scope Java Recursion Install Java Install JDK Java IDEs Java JDK Java JRE Java JVM Java Compiler Java File Compile Java Run Java JShell Basics Java Packages Java OOP Java Classes Java Objects Class Methods Constructors Constructor Overload this Keyword static Keyword final Keyword Encapsulation Inheritance Polymorphism Abstraction Access Modifiers super Keyword Method Overload Method Override Instanceof Operator Nested Classes Inner Classes Anonymous Classes Enums Records Array Basics Array Length Loop Arrays Multidimensional Arrays Array Copy Array Sorting Array Search ArrayList LinkedList HashSet TreeSet HashMap TreeMap Queue Interface Deque Interface Stack Class Iterator List Interface Set Interface Map Interface Collections Class Comparable Comparator Generics Basics Generic Classes Generic Methods Wildcards String Basics String Length String Methods String Concatenation String Compare String Search String Replace String Split String Format StringBuilder StringBuffer Text Blocks Regular Expressions Number Types Integer Class Float Class Double Class BigInteger BigDecimal Math Class Random Numbers Number Format Date Basics LocalDate LocalTime LocalDateTime ZonedDateTime Date Formatting Date Parsing Period Class Duration Class Java Instant Class Java Exception Basics Java try...catch Java Multiple Catch Blocks Java finally Block Java throw Keyword Java throws Keyword Java Checked Exceptions Java Unchecked Exceptions Java Custom Exceptions Java try-with-resources Java Exception Chaining
Ask about this post
AI Ask about this post

Ask questions about LocalDate and get answers drawn from it.

Signed-in readers only.