java
42 lines · 7 steps
Adding ISO-8601 durations across time zones in Java
A calculator that adds an ISO-8601 duration to an instant, handling calendar units through a zone so DST and month lengths stay correct.
Explained by
highlit
1package com.example.time;
2
3import java.time.Duration;
4import java.time.Instant;
5import java.time.LocalDateTime;
6import java.time.Period;
7import java.time.ZoneId;
8import java.time.format.DateTimeParseException;
9
10public final class DurationOffsetCalculator {
11
12 private final ZoneId zone;
13
14 public DurationOffsetCalculator(ZoneId zone) {
15 this.zone = zone;
16 }
17
18 public Instant addIso8601(Instant start, String iso8601) {
19 if (iso8601 == null || iso8601.isBlank()) {
20 throw new IllegalArgumentException("duration must not be blank");
21 }
22
23 int timeIndex = iso8601.indexOf('T');
24 String datePart = timeIndex < 0 ? iso8601 : iso8601.substring(0, timeIndex);
25 boolean hasCalendarUnits = datePart.length() > 1 || datePart.contains("Y") || datePart.contains("M") || datePart.contains("W") || datePart.contains("D");
26
27 try {
28 if (hasCalendarUnits) {
29 Period period = Period.parse(timeIndex < 0 ? iso8601 : datePart);
30 Duration time = timeIndex < 0 ? Duration.ZERO : Duration.parse("P" + iso8601.substring(timeIndex));
31 return LocalDateTime.ofInstant(start, zone)
32 .plus(period)
33 .plus(time)
34 .atZone(zone)
35 .toInstant();
36 }
37 return start.plus(Duration.parse(iso8601));
38 } catch (DateTimeParseException e) {
39 throw new IllegalArgumentException("invalid ISO-8601 duration: " + iso8601, e);
40 }
41 }
42}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Calendar units like months and days are zone-dependent, so they must be applied through a LocalDateTime rather than a fixed-length Duration.
- 2Splitting an ISO-8601 string at 'T' lets you route the date part to Period and the time part to Duration, which parse different halves.
- 3Wrapping parse calls to rethrow a domain-specific exception gives callers a clear, uniform failure contract.
Related explainers
ruby
module DeepImmutable module_function def deep_freeze(obj)
Deep-freezing config with recursive immutability
immutability
recursion
deep-copy
Intermediate
8 steps
java
@Component public class InventoryReconciliationJob { private static final Logger log = LoggerFactory.getLogger(InventoryReconciliationJob.class);
Distributed scheduled jobs with Spring locks
distributed-locking
scheduling
concurrency
Advanced
8 steps
java
import java.util.Comparator; import java.util.LinkedHashMap; import java.util.Map; import java.util.regex.Pattern;
Ranking the most frequent words in Java
streams
regex
grouping
Intermediate
7 steps
java
public final class HttpHeaders { private final NavigableMap<String, List<String>> values = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
A case-insensitive HTTP headers map in Java
case-insensitivity
multimap
immutability
Intermediate
7 steps
php
<?php namespace App\Reminders;
Scheduling reminders across time zones in PHP
timezones
datetime
immutability
Intermediate
7 steps
java
public record ServerConfig( String host, int port, Duration timeout,
Loading typed config into a Java record
records
factory-methods
parsing
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/adding-iso-8601-durations-across-time-zones-in-java-explained-java-e550/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.