Convert a timestamp between timezones
Take a date / time in one timezone and print its equivalent in another — for "what time is the 3pm Tokyo meeting in New York?", server-log normalization, and any cross-region scheduling task.
How to convert a timestamp between timezones in each shell
TZ=Asia/Tokyo date -d "2026-05-17 15:00 America/New_York"`TZ=NAME COMMAND` shifts JUST that command's timezone — the env var is one-shot, your shell's timezone is unchanged. Combined with `date -d "STAMP TZ"` (GNU parses the trailing TZ name and treats input as that zone), you get a clean "input in NYC, output in Tokyo" pipe. IANA timezone names like `Asia/Tokyo`, `America/New_York`, `Europe/London`, `UTC` — see `timedatectl list-timezones` or `ls /usr/share/zoneinfo/`.
TZ=Asia/Tokyo date -d "2026-05-17 15:00 America/New_York"env TZ=Asia/Tokyo date -d "2026-05-17 15:00 America/New_York"Fish does NOT honour `VAR=value command` inline assignment — must use `env VAR=value command` or `set -lx TZ Asia/Tokyo; date ...; set -e TZ`. This is a common bash → fish porting trip-up.
[TimeZoneInfo]::ConvertTimeBySystemTimeZoneId((Get-Date "2026-05-17 15:00"), "Eastern Standard Time", "Tokyo Standard Time")pwsh 7+ accepts both Windows zone IDs (`"Eastern Standard Time"`) AND IANA names (`"America/New_York"`) on Linux/macOS — but on Windows pwsh, only Windows IDs work unless tzdata is installed. `[TimeZoneInfo]::FindSystemTimeZoneById("America/New_York")` throws on stock Windows. For cross-platform scripts: pin to IANA on Linux/macOS, Windows IDs on Windows, OR use the .NET 8 `TimeZoneInfo.TryConvertIanaIdToWindowsId()` bridge (pwsh 7.4+).
powershell -NoProfile -Command "[TimeZoneInfo]::ConvertTimeBySystemTimeZoneId((Get-Date '2026-05-17 15:00'), 'Eastern Standard Time', 'Tokyo Standard Time')"cmd `tzutil /g` shows the current system timezone; `tzutil /l` lists all available Windows timezone IDs. cmd has no native conversion verb — shelling to pwsh is standard. `w32tm /tz` returns current TZ in a different format (used for time-sync diagnostics).
Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.
Gotchas & notes
- **IANA names vs Windows IDs — the #1 cross-platform timezone trap**. IANA (also called "tz database" or "zoneinfo") uses location-based names: `America/New_York`, `Europe/Paris`, `Asia/Tokyo`. Windows uses "display IDs": `Eastern Standard Time` (covers BOTH EST and EDT despite the misleading name), `Romance Standard Time` (France/Spain/Belgium), `Tokyo Standard Time`. They are NOT one-to-one — Windows has ~140 zones, IANA has ~600 (IANA splits when a region's history diverges, e.g. `America/Indiana/Indianapolis` vs `America/Indiana/Vincennes` for the Indiana counties that ran on different offsets). For cross-platform pipelines, IANA is the standard. Windows 10 1809+ optionally accepts IANA names if `Microsoft.Windows.TZData` package is installed; pwsh 7 on Linux/macOS speaks IANA natively. Conversion tables: `[TimeZoneInfo]::TryConvertIanaIdToWindowsId()` (pwsh 7.4+ / .NET 8+), or `windowsZones.xml` from CLDR.
- **Daylight saving — "EST" is not "EDT" and the name lies**. `Eastern Standard Time` on Windows is actually "the zone of NYC", which means it AUTOMATICALLY switches between EST (UTC-5) in winter and EDT (UTC-4) in summer — the name is historical and misleading. Same trap: `Pacific Standard Time` ≡ `America/Los_Angeles` (auto DST), `Romance Standard Time` ≡ `Europe/Paris` (auto DST). For ALWAYS-STANDARD-TIME (no DST): there is no Windows ID; on IANA use `Etc/GMT+5` (note the SIGN IS FLIPPED — `Etc/GMT+5` means UTC-5, per POSIX historical convention). For UTC always: `UTC` IANA or `UTC` Windows. NEVER hardcode an offset (`+0500`) for a region that observes DST — your script will be one hour off for half the year.
- **System-wide vs per-command timezone change**: `TZ=Asia/Tokyo date` shifts ONLY that command. To shift your whole shell session: `export TZ=Asia/Tokyo` (lasts until logout). To shift the SYSTEM permanently: `sudo timedatectl set-timezone Asia/Tokyo` (Linux systemd), `sudo systemsetup -settimezone Asia/Tokyo` (macOS), `tzutil /s "Tokyo Standard Time"` (Windows). The SHELL-level shift is what scripts want — never modify system TZ unless you're intentionally redating the host. `timedatectl list-timezones | grep -i tokyo` to discover the right IANA name; `tzutil /l | findstr /i tokyo` on Windows for the matching Windows ID.
- **Logging in UTC, displaying in local — the production pattern**: store / log timestamps in UTC (ISO 8601 with `Z` suffix: `2026-05-17T15:00:00Z`), convert at PRESENTATION time only. This avoids DST gaps (the 23-hour day each spring) and timezone surprises across replicated regions. systemd journal records in UTC by default; can display in local with `journalctl --output-fields=... --utc` or `TZ=Asia/Tokyo journalctl`. PostgreSQL: `TIMESTAMPTZ` stores UTC, applies `SESSION TIMEZONE` on display. The anti-pattern: `Asia/Tokyo` displayed-string stored as `VARCHAR` in a DB, then parsed back without TZ — irreversible information loss as soon as the user's session TZ differs.
- **Half-hour, 45-minute, and rolled-back zones — "all timezones are integer offsets" is a myth**. India: UTC+5:30 (one of several `:30` zones), Nepal: UTC+5:45 (a `:45`!), Newfoundland: UTC-3:30 (StA). Iran: UTC+3:30 in winter, +4:30 in summer. Lord Howe Island, Australia: UTC+10:30 in winter, +11 (full hour shift on DST!). North Korea reverted from UTC+8:30 back to UTC+9 in 2018 — meaning `date -d "2017-04-01 00:00 Asia/Pyongyang"` and `date -d "2019-04-01 00:00 Asia/Pyongyang"` produce different absolute UTC times despite the same local clock reading. ALWAYS use IANA tzdata, NEVER hardcode `(offset + 3600)` — the tzdata package gets updated continuously (last 12 months: Lebanon, Greenland, Mexico all had changes) and your scripts inherit the fixes if you stay on `Asia/Pyongyang` strings.
Related commands
Related tasks
- Parse a relative date like "next Friday" or "2 weeks ago"— Convert a human-readable relative-time expression ("3 days ago", "next Monday", "last quarter end") into an absolute date — for log filters, backfill ranges, scheduling, or any script that wants to accept user input the same way GitHub Actions cron does.
- Calculate days between two dates— Compute the integer number of days between two calendar dates — for SLA timers, log-window queries, license-expiry math, or backfill scripts that ask "how many days have passed since X?".
- Get the day of the week for a date— Print which weekday a given date falls on — for backup-window scripts ("only run on Sunday"), log rotation, conditional CI gates ("skip the weekly job on Friday"), or `cron` replacements that need day-of-week introspection.