What is the fall-back overlap in DST, how does java.time resolve an ambiguous local time by default, and how do you choose the other offset?
answer
- Fall-back = clock back, an hour repeats -> ambiguous
- Default picks the EARLIER offset (first occurrence)
- withLaterOffsetAtOverlap() = second occurrence
- Methods are no-ops outside an overlap
- isOverlap(); getOffsetBefore()/After(); duration is negative
basics
~20 sIn autumn, clocks go back so one hour of local time happens twice. A time like 02:30 is ambiguous. By default java.time picks the earlier of the two offsets. You can force the other one with withLaterOffsetAtOverlap() (or back with withEarlierOffsetAtOverlap()).
solid answer
~40 sDuring fall-back the clock is set back an hour, so the same wall-clock hour repeats; a local time inside it maps to two instants with two different offsets (e.g. +02:00 then +01:00). java.time must pick one when you build a ZonedDateTime from a local time: by default it chooses the earlier offset, i.e. the first occurrence (the summer/+02:00 reading). If you actually meant the second occurrence, call withLaterOffsetAtOverlap(); withEarlierOffsetAtOverlap() restores the default. These methods are no-ops outside an overlap. You detect an overlap with zone.getRules().getTransition(local).isOverlap(), which also exposes getOffsetBefore() and getOffsetAfter(). This matters when a timestamp without an offset is ambiguous, e.g. logs at 02:30 on the fall-back night could be an hour apart; always persist the offset or the instant, never bare local time.
code
java · 7 linesZoneId berlin = ZoneId.of("Europe/Berlin");
LocalDateTime amb = LocalDateTime.parse("2024-10-27T02:30");
ZonedDateTime first = amb.atZone(berlin); // default: earlier offset
ZonedDateTime second = first.withLaterOffsetAtOverlap();
System.out.println(first); // 2024-10-27T02:30+02:00[Europe/Berlin]
System.out.println(second); // 2024-10-27T02:30+01:00[Europe/Berlin]
System.out.println(Duration.between(first, second)); // PT1Hgo deeper
Knows autumn repeats an hour and that the time becomes ambiguous; can name that a method exists to pick the other one.
States the default (earlier offset) and uses withLaterOffsetAtOverlap correctly, knowing it is a no-op outside the overlap.
Designs storage to avoid ambiguity (persist offset/Instant), explains the two-instant mapping and how isOverlap/getOffsetBefore drive it.
Defines cross-service rules for ambiguous timestamps (canonical UTC storage, documented resolution policy) and audits log/scheduling pipelines for fall-back correctness.
**Recap of terms** (same as the gap question): a *LocalDateTime* is wall-clock numbers with no zone; a *ZoneId* names a region; a *ZoneOffset* is the UTC difference like `+01:00`; an *Instant* is an exact UTC point; *DST* shifts the clock twice a year. **The fall-back overlap.** When a zone falls back, e.g. at `03:00` the clock is set back to `02:00`, the wall-clock hour `02:00`–`02:59` happens **twice**: once before the change (still summer offset, say `+02:00`) and once after (winter offset `+01:00`). A `LocalDateTime` like `2024-10-27T02:30` is therefore **ambiguous**: it matches **two distinct instants** one hour apart. This repeated interval is the **overlap**. **Default resolution.** `ZonedDateTime.of(local, zone)` must pick one. The contract: in an overlap it uses the **earlier** offset, i.e. the **first** occurrence (the offset that applied *before* the transition, `getOffsetBefore()`). So `2024-10-27T02:30` resolves to `02:30+02:00` by default. **Choosing the other instant.** Two instance methods adjust an already-built `ZonedDateTime` when it sits in an overlap: - `withEarlierOffsetAtOverlap()` -> first occurrence, the earlier (pre-transition) offset. This is the default, so it mainly *re-asserts* the default. - `withLaterOffsetAtOverlap()` -> second occurrence, the later (post-transition) offset, `getOffsetAfter()`. Both are **no-ops if the time is not in an overlap**, so they are safe to call unconditionally. They do **not** change the wall-clock fields, only which offset (and thus which instant) is attached. **Detecting an overlap.** `zone.getRules().getTransition(local)` returns a `ZoneOffsetTransition`; `isOverlap()` is true for the repeated hour, and `getOffsetBefore()`/`getOffsetAfter()` give the two candidate offsets. `getDuration()` is **negative** for an overlap (the clock went back) and positive for a gap. **Why it matters.** A timestamp stored as bare local text (`2024-10-27 02:30`) is genuinely undecodable on the fall-back night, two real events an hour apart look identical. Consequences: duplicate or mis-ordered log lines, double-charged or skipped scheduled jobs, wrong durations. The fix is to **store the offset or the Instant** (e.g. `OffsetDateTime`/epoch), so the two occurrences stay distinct. If you only have local time and must guess, java.time's default (earlier offset) is the documented, deterministic choice, document that you rely on it. **Relation to the gap.** Gap and overlap are the two sides of a transition: a gap *removes* an hour and resolution **adds** time (uses the later offset); an overlap *repeats* an hour and resolution defaults to the **earlier** offset. The same `getTransition` API distinguishes them via `isGap()`/`isOverlap()`.
- If you store a fall-back-night event as a plain LocalDateTime string, what goes wrong and how do you fix it?It is ambiguous: two events an hour apart in the repeated hour serialize identically, so ordering/dedup/durations break. Fix by persisting the offset or the Instant (OffsetDateTime / epoch millis) instead of bare local time.
- What does getDuration() return for an overlap versus a gap?Positive (e.g. +1h) for a gap because time was added; negative (e.g. -1h) for an overlap because the clock went back. isGap()/isOverlap() are the clearer checks.
saying these in an interview costs you the question
- Saying the default picks the later offset (it picks the earlier)
- Thinking withLaterOffsetAtOverlap changes the wall-clock time (it changes only the offset/instant)
- Forgetting these methods are no-ops outside the overlap
- Storing fall-back timestamps as bare local time and expecting them to round-trip