TZ
Sets the timezone for the process, affecting how times are displayed and how time-related functions operate. Uses the IANA Time Zone Database identifiers. When not set, the system timezone from /etc/localtime is used.
Last updated:
TZ sets the timezone for a single process using IANA Time Zone Database names like `America/New_York` or `Europe/London` — never raw offsets like GMT+2, because those ignore daylight saving. It is the cleanest way to make one service run in a specific zone without changing the whole machine: set TZ and the C library's localtime(), plus most language runtimes, immediately honor it. The standard advice still holds — store and compute in UTC, convert at display time — but TZ is what you reach for when a cron job, log timestamp, or report must render in a particular local zone.
- Provider
- General / OS
- Category
- locale
- Set by
- Set via environment variable, system settings, or /etc/timezone
- Example
- America/New_York
How to set TZ
run one command in a zone
TZ=Asia/Tokyo dateDocker (Alpine needs tzdata)
RUN apk add --no-cache tzdata
ENV TZ=Europe/Oslodocker-compose
services:
app:
environment:
TZ: America/New_YorkWhat formats does TZ accept?
Three, and they look deceptively similar. The one you want is an IANA Time Zone Database name — Europe/Stockholm, America/New_York — resolved against the tzdata files under /usr/share/zoneinfo. POSIX additionally defines a rule string like EST5EDT,M3.2.0,M11.1.0 that encodes the DST schedule inline, and a leading-colon form (TZ=:Europe/Stockholm) that says "this is a file reference, not a rule string". The trap is that an unrecognized value doesn't error — TZ=CEST or TZ=UTC+2 silently degrades to UTC or to a fixed offset with no DST, and your timestamps drift by an hour twice a year. Abbreviations like EST are display strings, not zone identifiers.
# list valid names
timedatectl list-timezones | grep -i stockholm
# verify what a process will see
TZ=Europe/Stockholm date
TZ=Nonsense/Zone date # falls back silently — compare the outputWhen is TZ read?
At first use, and in many runtimes effectively once. glibc's tzset() reads TZ the first time a time function needs it; Node.js captures TZ at process start, and while recent V8 picks up some changes to process.env.TZ at runtime, cached Intl.DateTimeFormat objects keep their original zone — a reliable source of "it works in this function but not that one" bugs. The JVM reads user.timezone once at startup. The rule that always holds: set TZ before the process starts, never mutate it mid-flight.
There's a performance footnote that surprises people: when TZ is unset, glibc re-examines /etc/localtime on every localtime() call, which shows up as a wall of stat() syscalls in strace on logging-heavy services. Database and observability teams (the MySQL/Percona world documented this years ago) set TZ=:/etc/localtime or TZ=UTC explicitly just to pin the lookup.
Containers: UTC until proven otherwise
Practically every base image defaults to UTC, and that is the right default — convert at the edges, not in the infrastructure. When a containerized service genuinely must run in local time (report generation, cron schedules aligned to business hours), two things have to be true: TZ is set, and tzdata is installed. Alpine and distroless images ship without the database, so TZ silently does nothing until you add it:
FROM alpine:3.20
RUN apk add --no-cache tzdata
ENV TZ=Europe/StockholmFor scheduled jobs specifically, prefer the scheduler's own zone support over process TZ where it exists — Kubernetes CronJobs grew a timeZone field (stable in 1.27), and cronie supports CRON_TZ= per crontab. How cron expressions interact with zones and DST is covered in the cron expression guide; for wiring TZ into compose files, see the docker-compose environment variables guide.
The tzdata churn problem
Zone rules are political, and they change on short notice. Jordan abolished DST in October 2022 with weeks of warning, forcing emergency tzdata releases (2022e); Egypt reintroduced DST in 2023 on about six weeks' notice; tzdata 2022b renamed Europe/Kiev to Europe/Kyiv. Two operational consequences: pin and update the tzdata package in your images like any other dependency (an outdated zone file is a correctness bug, not a cosmetic one), and treat old zone aliases as deprecated-but-working — Europe/Kiev still resolves via the backward-compatibility links, but new code should use the canonical name. Java, .NET, and browsers each bundle their own copy of the database, so "the OS is updated" does not mean your runtime is.
Windows is a different system entirely
The Windows C runtime's TZ support predates the IANA database and only understands the POSIX-ish PST8PDT form — feed it America/Los_Angeles and you get garbage offsets. Windows itself tracks zones with its own names ("Pacific Standard Time") mapped to IANA via CLDR's windowsZones table. Cross-platform runtimes mostly insulate you: Node, Java, and Python's zoneinfo carry or locate their own IANA data, so TZ-with-IANA-names works inside those runtimes even on Windows. Native code and anything built directly on the MSVC runtime does not get that courtesy — if your app targets Windows natively, configure the zone through Windows settings, not TZ.
References
Frequently Asked Questions
I set TZ but the time is still UTC in my container. Why?
The timezone database is missing. Alpine and other slim images do not include tzdata, so the IANA zone you named cannot be resolved and the process stays on UTC. Install it (`apk add tzdata` / `apt-get install tzdata`) and make sure TZ uses a Region/City name, not an abbreviation like EST.
Stay up to date
Get notified about new guides, tools, and cheatsheets.