Using LocalDate.plusDays in Java
java localdate plusdays: Learn how LocalDate.plusDays works, why it returns a new instance, and how to handle month boundaries, negatives, and overflow in Java.
When you call java localdate plusdays, the method does not modify the original LocalDate. It returns a new LocalDate instance representing the result of adding the specified number of days. This is a direct consequence of the immutability design of the java.time package. Every arithmetic method in LocalDate produces a fresh instance, leaving the original object untouched.
import java.time.LocalDate; LocalDate today = LocalDate.of(2024, 3, 15); LocalDate later = today.plusDays(10); System.out.println(today); // 2024-03-15 System.out.println(later); // 2024-03-25
The plusDays method takes a long argument. It can accept positive, negative, or zero values. A negative value effectively subtracts days, which is useful when you need to compute a date in the past without switching to minusDays.
Why Ignoring the Return Value Causes Bugs
A common mistake is to call plusDays without assigning the result. Because LocalDate is immutable, the original variable remains unchanged. The method's return value is the only way to access the computed date.
LocalDate dueDate = LocalDate.of(2024, 12, 31); dueDate.plusDays(7); // result is discarded System.out.println(dueDate); // still 2024-12-31
The corrected version assigns the result back to the variable or to a new variable:
LocalDate dueDate = LocalDate.of(2024, 12, 31); dueDate = dueDate.plusDays(7); System.out.println(dueDate); // 2025-01-07
This behavior is consistent with other java.time classes like LocalDateTime, ZonedDateTime, and Instant. Once you internalize that these objects are value objects, you avoid a whole class of off-by-one and stale-state bugs.
How Month and Year Boundaries Are Handled
plusDays automatically normalizes the result across month and year boundaries. You do not need to manually check the length of the current month or whether the year is a leap year.
LocalDate endOfJanuary = LocalDate.of(2024, 1, 31); LocalDate februaryDate = endOfJanuary.plusDays(1); System.out.println(februaryDate); // 2024-02-01
Similarly, adding days across a leap year correctly accounts for February 29:
LocalDate beforeLeapDay = LocalDate.of(2024, 2, 28); LocalDate afterLeapDay = beforeLeapDay.plusDays(1); System.out.println(afterLeapDay); // 2024-02-29
The implementation relies on the internal epoch-day representation. Adding a day simply increments the epoch day and converts back to a LocalDate. This makes the operation independent of calendar month lengths and leap-year rules.
Using Negative Values and Subtracting Days
Passing a negative value to plusDays is equivalent to calling minusDays with the absolute value. This can be useful when the number of days to shift is computed dynamically and may be negative.
LocalDate start = LocalDate.of(2024, 6, 10); LocalDate shifted = start.plusDays(-15); System.out.println(shifted); // 2024-05-26
If you know the shift will always be negative, minusDays is more readable. But when the direction depends on runtime input, plusDays with a signed value keeps the logic in one place.
Comparing plusDays with ChronoUnit.DAYS
The java.time API offers another way to add days using ChronoUnit.DAYS.addTo():
import java.time.temporal.ChronoUnit; LocalDate start = LocalDate.of(2024, 3, 1); LocalDate end = ChronoUnit.DAYS.addTo(start, 30);
Both approaches produce the same result for LocalDate. The difference is that ChronoUnit.addTo works with any Temporal implementation, so it is more general. If you are only working with LocalDate, plusDays is more direct and slightly more readable. If you are writing generic code that accepts a Temporal, ChronoUnit is the appropriate abstraction.
Performance and Allocation Characteristics
Every call to plusDays creates a new LocalDate instance if the argument is non-zero. LocalDate is a small object that stores a single int for the day, a short for the month, and a short for the year. The allocation cost is low, and in most applications it will not be a bottleneck. However, if you are adding days inside a tight loop that runs millions of times, the repeated allocation can increase garbage collection pressure.
LocalDate current = LocalDate.of(2024, 1, 1); for (int i = 0; i < 1_000_000; i++) { current = current.plusDays(1); }
This loop performs one million allocations. An alternative is to use an int counter and convert to LocalDate only when needed, or to use LocalDate.ofEpochDay with an incrementing epoch day. The latter avoids repeated object creation if you only need the final date or a few intermediate values.
long epochDay = LocalDate.of(2024, 1, 1).toEpochDay(); for (int i = 0; i < 1_000_000; i++) { epochDay++; } LocalDate result = LocalDate.ofEpochDay(epochDay);
This version creates only two LocalDate instances. Use this pattern only when profiling shows that allocation is a real concern; premature optimization usually adds complexity without measurable benefit.
Handling Overflow and Edge Cases
The plusDays method accepts a long value, but the resulting date must fit within the valid LocalDate range. LocalDate supports years from -999999999 to 999999999. If adding days pushes the date outside this range, plusDays throws an ArithmeticException because the internal epoch-day calculation overflows.
LocalDate extreme = LocalDate.of(999999999, 12, 31); try { extreme.plusDays(1); } catch (ArithmeticException e) { // thrown when the result exceeds the supported range }
This behavior is intentional. The API fails fast rather than silently producing an incorrect date. In normal business applications, this edge case rarely occurs, but it is worth knowing when you process very large date ranges.
Another edge case is passing zero. plusDays(0) returns the same instance, not a copy. This is an optimization that is safe because the object is immutable.
LocalDate same = someDate.plusDays(0); System.out.println(same == someDate); // true
Null Handling and Defensive Coding
plusDays does not accept a null receiver. If the LocalDate variable is null, calling plusDays throws a NullPointerException. This is consistent with the rest of the java.time package, which rejects null arguments and receivers.
LocalDate date = null; date.plusDays(1); // NullPointerException
If you are working with dates that may be null, check for null before calling plusDays, or use Optional<LocalDate> to make the null state explicit. Avoid catching NullPointerException to handle missing dates; that hides the actual source of the problem.
Using plusDays in Stream Pipelines
When you need to generate a sequence of dates, plusDays composes naturally with Stream.iterate.
LocalDate start = LocalDate.of(2024, 1, 1); List<LocalDate> firstFiveDays = Stream.iterate(start, d -> d.plusDays(1)) .limit(5) .toList();
This produces the first five days of January 2024. The lambda d -> d.plusDays(1) is a pure function, which makes it safe for parallel streams. Because LocalDate is immutable, there is no shared mutable state to synchronize.
For more complex sequences, such as skipping weekends, you can combine plusDays with a filter. The immutability of LocalDate keeps the pipeline free of side effects, which simplifies reasoning about the code.