How to check the time-zone data of Go, Java, Node.js and Python (tzdata 2026e, part 2)
Each runtime reads its own copy: the C library and Python's zoneinfo use /usr/share/zoneinfo, Go uses $ZONEINFO, the system copy, $GOROOT/lib/time/zoneinfo.zip, then data compiled in with time/tzdata, Java uses the JDK's tzdb.dat, Node.js its built-in ICU data (process.versions.tz), and pytz its bundled copy (pytz.OLSON_VERSION). Check each by asking for a real offset (Winnipeg on 2026-11-02 must be UTC−05:00 with 2026e), or run ocimend tzcheck, which finds and tests every copy on a host or in a container.
One command for all of it
ocimend tzcheck finds every copy of the time-zone database on a host or inside a container and asks
each one for real offsets: Vancouver, Edmonton, Inuvik and Winnipeg on 2 November 2026, Casablanca on 15 October.
Version labels can lie (a backported package, a JDK patched with an updater, a Go program with its own copy); the
offsets cannot. It needs no account and sends nothing anywhere.
curl -fsSL https://ocimend.io/downloads/agent/install.sh | sh # Linux and macOS, checksum verified ocimend tzcheck # this machine: system data, JDKs, node, python, Go programs on PATH ocimend tzcheck /srv /app # also look for Go programs in these directories ocimend tzcheck --json # for scripts and inventories ocimend tzcheck --strict # exit 1 when a copy in use is stale: a CI or fleet gate
On a machine with tzdata 2025b, a Go program built with time/tzdata, OpenJDK 21, Node.js 22 and
Python 3.11 with the tzdata and pytz packages (every value below is real output):
$ ocimend tzcheck /srv/billing
ocimend tzcheck · vm · linux/amd64 · newest tz release known: 2026e
STALE system /usr/share/zoneinfo [2025b]
America/Vancouver 2026-11-02 −08:00 expected −07:00 WRONG (old rules)
America/Edmonton 2026-11-02 −07:00 expected −06:00 WRONG (old rules)
Africa/Casablanca 2026-10-15 +01:00 expected +00:00 WRONG (old rules)
America/Inuvik 2026-11-02 −07:00 expected −06:00 WRONG (old rules)
America/Winnipeg 2026-11-02 −06:00 expected −05:00 WRONG (old rules)
misses: British Columbia, Alberta, Morocco, Northwest Territories (Inuvik), Manitoba
used by: the C library (date, cron, most daemons), Go and Python's zoneinfo when present, most databases
STALE go-toolchain /usr/local/go1.24.7/lib/time/zoneinfo.zip [no version label]
America/Vancouver 2026-11-02 −08:00 expected −07:00 WRONG (old rules)
America/Edmonton 2026-11-02 −07:00 expected −06:00 WRONG (old rules)
Africa/Casablanca 2026-10-15 +01:00 expected +00:00 WRONG (old rules)
America/Inuvik 2026-11-02 −07:00 expected −06:00 WRONG (old rules)
America/Winnipeg 2026-11-02 −06:00 expected −05:00 WRONG (old rules)
misses: British Columbia, Alberta, Morocco, Northwest Territories (Inuvik), Manitoba
used by: Go programs on this machine when there is no system copy; and what time/tzdata embeds in programs built here
STALE java /usr/lib/jvm/java-21-openjdk-amd64/lib/tzdb.dat [2025b]
misses: British Columbia, Alberta, Morocco, Northwest Territories (Inuvik), Manitoba
used by: java.time and java.util.TimeZone in this JDK
STALE node /opt/node22/bin/node [2025c]
America/Vancouver 2026-11-02 −08:00 expected −07:00 WRONG (old rules)
America/Edmonton 2026-11-02 −07:00 expected −06:00 WRONG (old rules)
Africa/Casablanca 2026-10-15 +01:00 expected +00:00 WRONG (old rules)
America/Inuvik 2026-11-02 −07:00 expected −06:00 WRONG (old rules)
America/Winnipeg 2026-11-02 −06:00 expected −05:00 WRONG (old rules)
misses: British Columbia, Alberta, Morocco, Northwest Territories (Inuvik), Manitoba
ICU 78.2
used by: Intl, Date#toLocaleString and libraries built on them, in this Node.js
STALE python-zoneinfo /opt/app-venv/bin/python3 (Python 3.11.15) [no version label]
America/Vancouver 2026-11-02 −08:00 expected −07:00 WRONG (old rules)
America/Edmonton 2026-11-02 −07:00 expected −06:00 WRONG (old rules)
Africa/Casablanca 2026-10-15 +01:00 expected +00:00 WRONG (old rules)
America/Inuvik 2026-11-02 −07:00 expected −06:00 WRONG (old rules)
America/Winnipeg 2026-11-02 −06:00 expected −05:00 WRONG (old rules)
misses: British Columbia, Alberta, Morocco, Northwest Territories (Inuvik), Manitoba
tzdata package 2025.2 installed (used only when the system copy is missing)
used by: zoneinfo.ZoneInfo: the system copy on /usr/share/zoneinfo:/usr/lib/zoneinfo:/usr/share/lib/zoneinfo:/etc/zoneinfo, else the tzdata package
STALE pytz /opt/app-venv/bin/python3 [2025b]
America/Vancouver 2026-11-02 −08:00 expected −07:00 WRONG (old rules)
America/Edmonton 2026-11-02 −07:00 expected −06:00 WRONG (old rules)
Africa/Casablanca 2026-10-15 +01:00 expected +00:00 WRONG (old rules)
America/Inuvik 2026-11-02 −07:00 expected −06:00 WRONG (old rules)
America/Winnipeg 2026-11-02 −06:00 expected −05:00 WRONG (old rules)
misses: British Columbia, Alberta, Morocco, Northwest Territories (Inuvik), Manitoba
pytz 2025.2
used by: pytz.timezone(): pytz's own bundled copy, never the system's
Go programs (1)
/srv/billing/billing-api (go1.24.7) uses: system (/usr/share/zoneinfo)
STALE go-embedded /srv/billing/billing-api [no version label]
America/Vancouver 2026-11-02 −08:00 expected −07:00 WRONG (old rules)
America/Edmonton 2026-11-02 −07:00 expected −06:00 WRONG (old rules)
Africa/Casablanca 2026-10-15 +01:00 expected +00:00 WRONG (old rules)
America/Inuvik 2026-11-02 −07:00 expected −06:00 WRONG (old rules)
America/Winnipeg 2026-11-02 −06:00 expected −05:00 WRONG (old rules)
misses: British Columbia, Alberta, Morocco, Northwest Territories (Inuvik), Manitoba
6 copies in use miss 2026 changes: local times in those regions will be wrong. Fixes: https://ocimend.io/blog/check-time-zone-data-go-java-node-python
The same machine with the system data replaced by 2026e (compiled from the IANA source) and nothing else changed:
the system copy passes, the Go program now reads it (its own embedded copy is still old, but no longer used),
and the JDK still fails, so --strict exits 1.
$ ZONEINFO=/opt/tz-2026e ocimend tzcheck --no-exec --strict /srv/billing
ocimend tzcheck · vm · linux/amd64 · newest tz release known: 2026e
OK system /opt/tz-2026e [2026e]
America/Vancouver 2026-11-02 −07:00 expected −07:00 ok
America/Edmonton 2026-11-02 −06:00 expected −06:00 ok
Africa/Casablanca 2026-10-15 +00:00 expected +00:00 ok
America/Inuvik 2026-11-02 −06:00 expected −06:00 ok
America/Winnipeg 2026-11-02 −05:00 expected −05:00 ok
used by: the C library (date, cron, most daemons), Go and Python's zoneinfo when present, most databases
STALE java /usr/lib/jvm/java-21-openjdk-amd64/lib/tzdb.dat [2025b]
misses: British Columbia, Alberta, Morocco, Northwest Territories (Inuvik), Manitoba
used by: java.time and java.util.TimeZone in this JDK
Go programs (1)
/srv/billing/billing-api (go1.24.7) uses: $ZONEINFO (/opt/tz-2026e)
STALE go-embedded /srv/billing/billing-api [no version label]
America/Vancouver 2026-11-02 −08:00 expected −07:00 WRONG (old rules)
America/Edmonton 2026-11-02 −07:00 expected −06:00 WRONG (old rules)
Africa/Casablanca 2026-10-15 +01:00 expected +00:00 WRONG (old rules)
America/Inuvik 2026-11-02 −07:00 expected −06:00 WRONG (old rules)
America/Winnipeg 2026-11-02 −06:00 expected −05:00 WRONG (old rules)
misses: British Columbia, Alberta, Morocco, Northwest Territories (Inuvik), Manitoba
1 copy in use misses 2026 changes: local times in those regions will be wrong. Fixes: https://ocimend.io/blog/check-time-zone-data-go-java-node-python
exit 1
Who reads which copy
| Runtime | Where its time-zone data comes from | How to see the version | How to fix |
|---|---|---|---|
| C library, shell, cron, most daemons | /usr/share/zoneinfo (or $TZDIR) | head -1 /usr/share/zoneinfo/tzdata.zi | upgrade the tzdata package; restart long-running services |
| Go | $ZONEINFO → system zoneinfo → $GOROOT/lib/time/zoneinfo.zip → its own copy (time/tzdata) | not visible in go version -m: ask the program, or ocimend tzcheck | system tzdata in the image, or rebuild with a Go release whose data is 2026e |
| Java | the JDK's lib/tzdb.dat, never the system's | ZoneRulesProvider.getVersions("UTC") | a JDK update with 2026e, or the vendor's time-zone updater applied to the JDK |
| Node.js | ICU data built into the node binary | node -p process.versions.tz | a Node.js release with 2026e in its ICU data |
Python zoneinfo | system zoneinfo, else the tzdata package | test an offset (it has no version) | upgrade the system tzdata; tzdata ≥ 2026.5 for images without one |
Python pytz | its own bundled copy, never the system's | pytz.OLSON_VERSION | pip install -U pytz (2026.5 = 2026e) |
1. The system copy (hosts and images)
$ head -1 /usr/share/zoneinfo/tzdata.zi # version 2025b $ dpkg-query -W tzdata tzdata 2025b-0ubuntu0.24.04.1 $ zdump -v -c 2026,2027 America/Winnipeg America/Winnipeg -9223372036854775808 = NULL America/Winnipeg -9223372036854689408 = NULL America/Winnipeg Sun Mar 8 07:59:59 2026 UT = Sun Mar 8 01:59:59 2026 CST isdst=0 gmtoff=-21600 America/Winnipeg Sun Mar 8 08:00:00 2026 UT = Sun Mar 8 03:00:00 2026 CDT isdst=1 gmtoff=-18000 America/Winnipeg Sun Nov 1 06:59:59 2026 UT = Sun Nov 1 01:59:59 2026 CDT isdst=1 gmtoff=-18000 America/Winnipeg Sun Nov 1 07:00:00 2026 UT = Sun Nov 1 01:00:00 2026 CST isdst=0 gmtoff=-21600 America/Winnipeg 9223372036854689407 = NULL America/Winnipeg 9223372036854775807 = NULL $ TZ=America/Winnipeg date -d "2026-11-02 12:00 UTC" Mon Nov 2 06:00:00 CST 2026 --- with 2026e $ zdump -v -c 2026,2027 America/Winnipeg America/Winnipeg -9223372036854775808 = NULL America/Winnipeg -9223372036854689408 = NULL America/Winnipeg Sun Mar 8 07:59:59 2026 UT = Sun Mar 8 01:59:59 2026 CST isdst=0 gmtoff=-21600 America/Winnipeg Sun Mar 8 08:00:00 2026 UT = Sun Mar 8 03:00:00 2026 CDT isdst=1 gmtoff=-18000 America/Winnipeg Sun Nov 1 06:59:59 2026 UT = Sun Nov 1 01:59:59 2026 CDT isdst=1 gmtoff=-18000 America/Winnipeg Sun Nov 1 07:00:00 2026 UT = Sun Nov 1 02:00:00 2026 EST isdst=0 gmtoff=-18000 America/Winnipeg 9223372036854689407 = NULL America/Winnipeg 9223372036854775807 = NULL $ TZ=America/Winnipeg date -d "2026-11-02 12:00 UTC" Mon Nov 2 07:00:00 EST 2026
Read the last two transitions: with 2025b, Winnipeg falls back to CST (UTC−06) on 1 November. With 2026e, the same instant is a change of name only (CDT → EST) and the offset stays −05:00. Note the new abbreviations: with 2026e data Vancouver shows MST, Edmonton and Inuvik CST, Winnipeg EST.
Package managers say the same thing: dpkg-query -W tzdata, apk info -v tzdata,
rpm -q tzdata. In a container, run them inside the image: the host's package is irrelevant to it.
A trap when compiling the data yourself: the 2026b release notes warn that zic versions 2023d
to 2026a, in their default "slim" mode, produce a Vancouver file some readers misread; compile with
zic -b fat when your zic is older than 2026b.
2. Go
go version -m shows the toolchain and modules, but not whether the program carries time-zone data or how
old it is:
$ go version -m /srv/billing/billing-api /srv/billing/billing-api: go1.24.7 path example.com/billing mod example.com/billing (devel) build -buildmode=exe $ ./billing-api 2026-11-02 06:00 CST -06:00 $ ZONEINFO=/opt/tz-2026e ./billing-api 2026-11-02 07:00 EST -05:00
This program imports time/tzdata, so it carries the data of the toolchain that built it (Go 1.24.7
here, older than 2026e). It still prefers the system copy when one exists, which is why pointing
$ZONEINFO at 2026e fixes it without a rebuild. In a scratch or distroless-static image there
is no system copy: the embedded data is what runs, and only a rebuild with a newer Go release (or adding
/usr/share/zoneinfo to the image) fixes it. Without time/tzdata and without system data,
time.LoadLocation fails and code that ignores the error silently uses UTC.
- 01$ZONEINFOif set
- 02System/usr/share/zoneinfo
- 03$GOROOT zipmachines with Go installed
- 04time/tzdatacompiled into the program
3. Java
Every JDK carries its own lib/tzdb.dat, whatever the host or image has. A ten-line check (Java 11+
runs a single source file directly):
// TzCheck.java
import java.time.*;
import java.time.zone.ZoneRulesProvider;
public class TzCheck {
public static void main(String[] a) {
System.out.println("tzdb version: " + ZoneRulesProvider.getVersions("UTC").lastKey());
Instant i = Instant.parse("2026-11-02T12:00:00Z");
for (String z : new String[] {"America/Vancouver", "America/Edmonton", "America/Inuvik", "America/Winnipeg"})
System.out.println(z + " " + i.atZone(ZoneId.of(z)).getOffset());
}
}
$ java TzCheck.java
tzdb version: 2025b
America/Vancouver -08:00
America/Edmonton -07:00
America/Inuvik -07:00
America/Winnipeg -06:00
Expected with 2026e: −07:00, −06:00, −06:00, −05:00. ocimend tzcheck reads the version straight from
each JDK's tzdb.dat (JAVA_HOME, /usr/lib/jvm, /opt/java and the java
on PATH), so it also finds JDKs nothing is running yet. Fix: the JDK update that carries 2026e, or the vendor's
time-zone updater tool run against that JDK, then restart the JVMs.
4. Node.js
Node.js formats dates with the ICU library compiled into the node binary, so the system's data does
not matter:
$ node -p process.versions.tz
$ node -e 'for (const z of ["America/Vancouver","America/Edmonton","America/Inuvik","America/Winnipeg"])
console.log(z, new Intl.DateTimeFormat("en-CA", {timeZone: z, timeZoneName: "longOffset", hour: "2-digit", minute: "2-digit"})
.format(new Date("2026-11-02T12:00:00Z")))'
2025c
America/Vancouver 04:00 a.m. GMT-08:00
America/Edmonton 05:00 a.m. GMT-07:00
America/Inuvik 05:00 a.m. GMT-07:00
America/Winnipeg 06:00 a.m. GMT-06:00
Libraries that format through Intl (date-fns-tz, Luxon, Day.js plugins) inherit this. Libraries that
bundle their own data (moment-timezone) have a version of their own to update. Fix: a Node.js release whose
process.versions.tz is 2026e or newer.
5. Python
$ python3 -c '
from zoneinfo import ZoneInfo; from datetime import datetime, timezone; import importlib.metadata as m, pytz
at = datetime(2026, 11, 2, 12, tzinfo=timezone.utc)
print("zoneinfo :", at.astimezone(ZoneInfo("America/Winnipeg")).strftime("%Y-%m-%d %H:%M %Z %z"))
print("tzdata pkg:", m.version("tzdata"), "| pytz:", pytz.__version__, "OLSON_VERSION", pytz.OLSON_VERSION)
print("pytz :", at.astimezone(pytz.timezone("America/Winnipeg")).strftime("%Y-%m-%d %H:%M %Z %z"))'
zoneinfo : 2026-11-02 06:00 CST -0600
tzdata pkg: 2025.2 | pytz: 2025.2 OLSON_VERSION 2025b
pytz : 2026-11-02 06:00 CST -0600
Two independent copies: zoneinfo uses the system data first (the tzdata package only when
there is none, as in slim or distroless images), while pytz always uses its own. Upgrading the OS fixes
the first and not the second. The PyPI versions map to releases: 2026.5 is 2026e.
In Kubernetes and containers
The binary is static: copy it into a running container and run it there, where the application's data is.
kubectl cp ./ocimend my-pod:/tmp/ocimend kubectl exec my-pod -- /tmp/ocimend tzcheck --no-exec /app # without a live container: scan the image on ocimend.io (every report now shows its time-zone data)
Use --no-exec inside production pods to only read files (it skips starting node or
python); the system copy, Go programs and JDKs are still checked.
Check your browser now
Your browser has its own copy too: the online check tests it with the same offsets.
Questions
Is checking the OS package enough?
How do I know if a Go binary embeds time zone data?
Which Python package version is tzdata 2026e?
Does ocimend tzcheck send anything?
What does --strict do?
Sources
- IANA tz database releases (NEWS)
- Go time.LoadLocation
- Go time/tzdata
- Java ZoneRulesProvider
- Node.js process.versions
- Python zoneinfo data sources
- pytz
More guides
- tzdata 2026e: on 1 November, containers with old time-zone data put four Canadian clocks an hour off
- How to build FIPS-enabled containers (FIPS 140-3)
- OCImend FIPS containers: what you get and how they are verified
- How to fix container image CVEs automatically
- A container vulnerability pipeline that ends in a fix, not a report
- "No fix available": what to do about unfixable container CVEs
- Base image or your layers? Finding which Dockerfile step introduced a CVE
- EU Cyber Resilience Act for container images: SBOM, VEX and 24-hour reporting
- Rolling out a patched container image without changing your registry or your manifests