A ZoneRules instance maps local date-time values to valid offsets; a gap has no valid offset and an overlap can have two.
Java daylight-saving transitions: reject gaps and choose overlaps
The complete program targets Java 8. Compile it as one source file; its output is checked against the lesson.
A local appointment is not yet an instant
A scheduler accepting a local appointment needs a region zone and a rule for daylight-saving changes. Automatically adjusting a nonexistent time can move a booking away from what the customer entered. Reject it when the application contract requires an exact local time.
This fixture uses known 2024 transitions in Europe/Paris. The March appointment has no matching offset. The October appointment has two, and each creates a different Instant from the same local fields. The region rules come from the runtime time-zone database; governments can change future rules.
Make the overlap policy visible
An offset is a numeric displacement, not a region rule. Persist the selected instant and the region when both execution and later display matter. If a future appointment must stay at a civil time even after a rule change, store that civil intent separately and define when it is recalculated.
The program displays both overlap instants rather than silently selecting one. A production input flow should ask for or clearly specify the earlier or later offset. A blanket UTC conversion before examining the local-time ambiguity loses evidence the service needs to make that choice.
Working program
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.List;
public class AppointmentOffsets {
public static void main(String[] args) {
ZoneId zone = ZoneId.of("Europe/Paris");
LocalDateTime gap = LocalDateTime.parse("2024-03-31T02:30:00");
LocalDateTime overlap = LocalDateTime.parse("2024-10-27T02:30:00");
System.out.println("gap offsets=" + zone.getRules().getValidOffsets(gap).size());
List<ZoneOffset> offsets = zone.getRules().getValidOffsets(overlap);
System.out.println("overlap offsets=" + offsets.size());
for (ZoneOffset offset : offsets) System.out.println(overlap.toInstant(offset));
}
}Output
gap offsets=0
overlap offsets=2
2024-10-27T00:30:00Z
2024-10-27T01:30:00ZCosts and boundaries
The fixture stores at most two offsets per query. Region rules and their cached data are runtime resources; do not model them as a fixed application-owned lookup table for every future date.
Common Mistakes
- Do not treat LocalDateTime as a unique instant.
- A fixed ZoneOffset does not include regional daylight-saving rules.
- Test gaps and overlaps separately.
Read next
Java date and time: local schedules versus instants, Java Clock: test expiry without sleeping, Configuration properties.
