java
35 lines · 6 steps
Converting timestamps across time zones in Java
A utility class that treats epoch millis as an absolute instant and projects it into any zone without shifting the underlying moment.
Explained by
highlit
1import java.time.Instant;
2import java.time.ZoneId;
3import java.time.ZonedDateTime;
4import java.time.format.DateTimeFormatter;
5
6public final class TimeZoneConverter {
7
8 private static final DateTimeFormatter ISO = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
9
10 public static ZonedDateTime fromEpochMillis(long epochMillis, ZoneId zone) {
11 return Instant.ofEpochMilli(epochMillis).atZone(zone);
12 }
13
14 public static long toEpochMillis(ZonedDateTime dateTime) {
15 return dateTime.toInstant().toEpochMilli();
16 }
17
18 public static ZonedDateTime convertZone(ZonedDateTime source, ZoneId target) {
19 return source.withZoneSameInstant(target);
20 }
21
22 public static String describe(long epochMillis, ZoneId zone) {
23 ZonedDateTime local = fromEpochMillis(epochMillis, zone);
24 return String.format("%s (%s) offset=%s",
25 local.format(ISO),
26 zone.getId(),
27 local.getOffset());
28 }
29
30 public static long shiftAcrossZones(long epochMillis, ZoneId from, ZoneId to) {
31 ZonedDateTime original = fromEpochMillis(epochMillis, from);
32 ZonedDateTime shifted = convertZone(original, to);
33 return toEpochMillis(shifted);
34 }
35}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1An epoch instant is absolute; a ZoneId only changes how that same moment is displayed.
- 2java.time types are immutable, so conversions return new objects rather than mutating in place.
- 3withZoneSameInstant reframes a time without altering the point on the timeline it represents.
Related explainers
java
public record AppConfig( String host, int port, String databaseUrl,
Loading typed config from env vars in Java
records
configuration
environment-variables
Intermediate
6 steps
python
from datetime import datetime, timezone _INTERVALS = ( ("year", 60 * 60 * 24 * 365),
Building a human-friendly time_ago helper
datetime
timezones
formatting
Intermediate
5 steps
java
public class RequestThrottler { private final Semaphore permits; private final long acquireTimeoutMillis;
Bounding concurrency with a Semaphore in Java
concurrency
semaphore
rate-limiting
Intermediate
6 steps
java
public List<DailySample> backfillMissingDates(List<DailySample> samples, double fillValue) { if (samples.isEmpty()) { return List.of(); }
Backfilling gaps in a daily time series in Java
streams
time-series
treemap
Intermediate
5 steps
java
public final class Ulid { private static final char[] ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ".toCharArray(); private static final SecureRandom RANDOM = new SecureRandom();
How to generate a ULID in Java
base32-encoding
bit-manipulation
identifiers
Intermediate
8 steps
java
@RestController @RequestMapping("/api/reports") public class ReportController {
Header-driven endpoints in a Spring controller
rest api
request headers
dependency injection
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/converting-timestamps-across-time-zones-in-java-explained-java-c3a2/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.