Why does `date.plusDays(1);` on its own line have no effect, and how do you fix it?
answer
- Methods return a new object, never mutate
- date.plusDays(1); with no '=' is a no-op
- Reassign: date = date.plusDays(1)
- Treat like String: s.toUpperCase() alone is useless
- Calendar.add mutated; java.time does not
basics
~20 sAll java.time types are immutable, so plusDays builds and returns a NEW date instead of changing the old one. If you ignore the returned value, nothing changes. Fix it by assigning the result: date = date.plusDays(1).
solid answer
~40 sEvery java.time type (LocalDate, LocalDateTime, Instant, Duration, etc.) is immutable: methods like plusDays, minusHours, and withYear never mutate the receiver. They compute a new instance and return it. So a statement like date.plusDays(1); creates a new LocalDate and immediately throws it away because the return value is unused; the original date variable still points at the old value. This is the single most common java.time bug, and it compiles cleanly because the method genuinely returns a value you are free to ignore. The fix is to capture the result, usually by reassigning: date = date.plusDays(1); or by chaining and assigning the final result. The same rule applies to all the plus/minus/with families and to Duration/Period arithmetic.
go deeper
Knows java.time objects don't change in place and that you must reassign the result of plusDays/minusX.
Explains the immutable return-new-instance pattern, why it compiles silently, and contrasts it with legacy Calendar.add mutation.
Connects immutability to thread-safety/safe-sharing trade-offs and points to lint tooling (Error Prone CheckReturnValue, IDE inspections) that catches the discarded-result bug in CI/review.
Frames immutability as a deliberate value-type design decision (JSR-310), discusses defensive-copy elimination and how to enforce no-ignored-return as a team-wide static-analysis rule.
## The setup `java.time` is the modern date-and-time API introduced in Java 8 (JSR-310), living in the `java.time` package: `LocalDate` (a date with no time), `LocalTime`, `LocalDateTime`, `Instant` (a point on the timeline in UTC), `ZonedDateTime`, plus the amounts `Duration` (seconds/nanos) and `Period` (years/months/days). ## What 'immutable' means An object is **immutable** when, once created, its internal state can never change. There is no setter, no method that alters a field. Every `java.time` type is immutable. So a method whose name suggests a change — `plusDays`, `minusHours`, `withYear`, `truncatedTo` — cannot actually change the object you call it on. Instead it follows a pattern: read this object's fields, compute the new values, **construct and return a brand-new object**, and leave the original untouched. ## Why `date.plusDays(1);` does nothing Consider: ```java LocalDate date = LocalDate.of(2026, 1, 1); date.plusDays(1); // <-- bug System.out.println(date); // prints 2026-01-01, NOT 2026-01-02 ``` The call `date.plusDays(1)` does run. It builds a new `LocalDate` representing 2026-01-02 and returns it. But the statement does nothing with the return value — there is no `=`, no further call — so the new object is immediately eligible for garbage collection. The variable `date` was never reassigned; it still references the original 2026-01-01 object (which could not have changed because the type is immutable). This compiles without warning because a method returning a value you choose to ignore is perfectly legal Java. The compiler does not know you *intended* to use it. (This is exactly the trap people coming from `java.util.Date`/`Calendar` fall into, because `Calendar.add(...)` *did* mutate in place.) ## The fix Capture the returned value: ```java date = date.plusDays(1); // reassign ``` or, when chaining, assign the final result of the chain: ```java LocalDate due = date.plusMonths(1).plusDays(15); ``` The receiver `date` is still unchanged; `due` holds the new value. ## Why immutability was chosen 1. **Thread safety** — an immutable object can be shared across threads with zero synchronization, because no thread can change it. 2. **Safe sharing / no defensive copies** — you can hand a `LocalDate` to a caller without fearing they mutate your copy. 3. **Predictability** — a value can't change underneath you between two lines of code. The trade-off is exactly this gotcha: you must always *use the return value*. A useful habit is to treat `java.time` types like `String` (also immutable): `s.toUpperCase();` alone is equally useless. ## Spotting it in review Any line that is *just* `x.plusXxx(...)` / `x.minusXxx(...)` / `x.withXxx(...)` / `x.truncatedTo(...)` with no assignment and no further use is almost certainly a bug. Some linters (e.g. Error Prone's `CheckReturnValue`, IntelliJ inspections) flag ignored return values of `@CheckReturnValue`-annotated or known-pure methods.
- Does the same discarded-result trap apply to Duration and Period?Yes. Duration and Period are immutable too, so duration.plusMinutes(5); with no assignment is also a no-op; you must capture the returned amount.
- Why does this code still compile without any warning?Ignoring a method's return value is legal Java; the compiler has no idea you intended to use it. Only external tools/inspections (Error Prone @CheckReturnValue, IDE inspections) flag it.
saying these in an interview costs you the question
- Believing plusDays/minusX/withX mutate the receiver
- Thinking the compiler will warn you about the discarded result
- Confusing java.time behavior with legacy Calendar.add()
- Saying you must call a 'commit' or 'apply' method afterward