如何将Java TimeInterval高效拆分为单一DST状态的子间隔
Hey there! That hourly iteration trick works, but it gets painfully slow for long time intervals—totally get why you’re hunting for a better approach. Instead of checking every single hour, we can directly target the DST transition moments in your target timezone and split the original interval at those points. Here’s how to do it efficiently:
Core Idea
DST transitions happen at specific, predictable times (usually twice a year for most timezones). Instead of scanning every hour, we:
- Find all DST transition dates that fall within the original
TimeInterval's start and end. - Combine these transition points with the interval's start/end, then sort them chronologically.
- Create sub-intervals between each consecutive pair of points—each of these will be entirely in either DST or standard time, no exceptions.
Step-by-Step Implementation (Java)
I’ll use Java’s modern java.time API here (it’s way better for timezone/DST logic than the old Date/Calendar mess), but we’ll convert back to your TimeInterval class at the end.
1. Set Up & Convert to Modern Types
First, convert your existing Date objects to ZonedDateTime so we can work with timezones properly:
// Your existing TimeInterval class class TimeInterval { Date start; Date end; // Add getters/setters as needed } // Define your target timezone (e.g., ZoneId.of("America/New_York")) ZoneId targetTimeZone = ZoneId.systemDefault(); // Or your specific zone // Convert Date to ZonedDateTime for timezone operations TimeInterval originalInterval = ...; // Your input interval ZonedDateTime startZdt = originalInterval.start.toInstant().atZone(targetTimeZone); ZonedDateTime endZdt = originalInterval.end.toInstant().atZone(targetTimeZone);
2. Collect All Split Points
We’ll grab all DST transitions that lie within our interval, plus the original start/end points:
ZoneRules zoneRules = targetTimeZone.getRules(); List<Instant> splitPoints = new ArrayList<>(); // Add original interval boundaries first splitPoints.add(startZdt.toInstant()); splitPoints.add(endZdt.toInstant()); // Add historical transitions that fall inside the interval for (ZoneOffsetTransition transition : zoneRules.getTransitions()) { Instant transitionInstant = transition.getInstant(); if (transitionInstant.isAfter(startZdt.toInstant()) && transitionInstant.isBefore(endZdt.toInstant())) { splitPoints.add(transitionInstant); } } // Add future transitions (in case your interval goes beyond pre-defined historical data) Instant nextTransition = zoneRules.nextTransition(startZdt.toInstant()); while (nextTransition != null && nextTransition.isBefore(endZdt.toInstant())) { splitPoints.add(nextTransition); nextTransition = zoneRules.nextTransition(nextTransition); }
3. Sort & Create Sub-Intervals
Sort the split points, then create sub-intervals between each pair:
// Sort points in chronological order Collections.sort(splitPoints); List<TimeInterval> dstSubIntervals = new ArrayList<>(); for (int i = 0; i < splitPoints.size() - 1; i++) { Instant subStart = splitPoints.get(i); Instant subEnd = splitPoints.get(i + 1); // Convert back to Date for your TimeInterval class TimeInterval subInterval = new TimeInterval(); subInterval.start = Date.from(subStart); subInterval.end = Date.from(subEnd); dstSubIntervals.add(subInterval); }
4. Optional: Verify DST Consistency
Just to be safe, you can confirm each sub-interval stays in a single DST state:
for (TimeInterval sub : dstSubIntervals) { ZonedDateTime subStartZdt = sub.start.toInstant().atZone(targetTimeZone); ZonedDateTime subEndZdt = sub.end.toInstant().atZone(targetTimeZone); boolean startsInDst = subStartZdt.isDaylightSavings(); boolean endsInDst = subEndZdt.isDaylightSavings(); // Since we split at transitions, these should always match assert startsInDst == endsInDst : "Sub-interval crosses DST transition!"; }
Why This Is Way More Efficient
Instead of iterating every hour (O(n) where n is the number of hours in your interval), we only process the DST transitions in the window—usually 2 per year, so it’s practically O(1) even for multi-year intervals. The ZoneRules class handles all the messy edge cases, like historical DST rule changes or future planned transitions.
Legacy API Note (If You Can’t Use java.time)
If you’re stuck with Date/Calendar, you can use TimeZone.getDSTSavings() and check for offset changes, but you’ll have to search for transitions in larger jumps (like month-by-month) instead of hour-by-hour to keep it efficient. But I’d strongly recommend upgrading to java.time—it’s part of Java 8+ and fixes almost all the old date API’s flaws.
内容的提问来源于stack exchange,提问作者hrabinowitz

