iKit
Tutorial · 9 min read ·

Compare Logs Across UTC, PST, and JST in One View (2026)

Learn how to compare logs across UTC, PST, and JST in one view: normalize to Unix time, then convert cleanly with IANA zone names and ISO 8601.

Compare Logs Across UTC, PST, and JST in One View (2026)

Compare Logs Across UTC, PST, and JST in One View

You have three log files: an API server writing UTC, a developer's laptop in Pacific time, and a regional node in Tokyo. A user reports a failed request, and you need to line up what each system did in the same second. Comparing logs across UTC, PST, and JST by eye is where bugs hide — an hour here, a calendar day there. The reliable fix is to stop reading the printed time and start reading the Unix timestamp underneath it.

TL;DR

  • Normalize every log line to Unix time first; epoch seconds are zone-agnostic.
  • Sort by epoch, then render that one number in UTC, PST, and JST.
  • JST is UTC+9 with no DST; Pacific flips between PST (-8) and PDT (-7).
  • Use IANA names like America/Los_Angeles, never a hard-coded offset.
  • Log in UTC with an ISO 8601 string ending in Z to avoid the whole problem.

Why comparing logs across time zones goes wrong

Most cross-zone log bugs are not exotic. They come from treating a printed timestamp as if it were the actual moment, when it is really a moment plus a hidden offset you forgot to account for.

The "off by 3 hours" bug nobody catches in review

A log line like 14:05:22 tells you nothing on its own. Fourteen-oh-five where? If the API logs UTC and your laptop logs Pacific, two lines that look three hours apart may be the exact same instant. Reviewers skim right past this because both timestamps look plausible. The only way to be sure is to convert both to a common reference and compare the numbers, not the wall-clock strings.

PST is not "Pacific Time" (and why that matters)

People say "PST" to mean "the time in California," but PST specifically means UTC-8, the standard (winter) offset. From the second Sunday in March to the first Sunday in November, the Pacific zone is actually on PDT, which is UTC-7. If you hard-code -8 for a June log, every line is off by an hour. This is why you reach for the zone region (America/Los_Angeles) and let the time library decide whether PST or PDT applies on that date.

When your servers disagree about "now"

A request can be logged at 2026-06-08T20:05:00Z on the API, 13:05 on a Pacific worker, and the next calendar day in Tokyo. Because JST is 16 hours ahead of PDT, an evening event in California is often already "tomorrow" in Japan. Sorting log lines alphabetically by their printed timestamp scrambles the real order. Sorting by epoch never does.

How to compare logs from UTC, PST, and JST in one view

The workflow is always the same three moves: normalize, convert, align. Do them in that order and the time zones stop fighting you.

Step 1: normalize everything to Unix time

Unix time is the number of seconds since 1970-01-01T00:00:00Z. It carries no offset, so it is the same integer everywhere on Earth at a given instant. Parse each log line, extract its timestamp with its offset, and reduce it to one epoch number. If a line shows 13:05:00-07:00, that is the same instant as 20:05:00Z — per RFC 3339, the offset is "local time minus UTC," so you subtract it to get UTC.

Paste any of these into a converter to confirm they collapse to the same number:

2026-06-08T20:05:00Z        -> 1780949100
2026-06-08T13:05:00-07:00   -> 1780949100
2026-06-09T05:05:00+09:00   -> 1780949100

All three are the same instant. The Unix Timestamp Converter turns each of these strings into the epoch seconds and back, so you can verify alignment without trusting your mental arithmetic.

Step 2: convert the epoch to each zone

Once every event is an epoch number, rendering it in three zones is a display concern. In JavaScript a Date always holds a UTC instant internally; Intl.DateTimeFormat simply paints that instant in whatever zone you name, as the MDN docs describe.

const epoch = 1780949100;       // seconds
const d = new Date(epoch * 1000);

const show = (tz) =>
  new Intl.DateTimeFormat("en-US", {
    timeZone: tz,
    dateStyle: "short",
    timeStyle: "medium",
    timeZoneName: "short",
  }).format(d);

show("UTC");                 // UTC view
show("America/Los_Angeles"); // PST or PDT, auto
show("Asia/Tokyo");          // JST

Note you never write -8 or +9. You name the region and the runtime applies the correct rule for that date.

Step 3: line them up on one axis

Now build a single table keyed by epoch. Each row is one event; each column is the same instant in a different zone. Because the rows share an epoch key, you can interleave lines from three files and sort the merged stream by that key. The result reads as one chronological story.

Zone IANA name Standard offset Observes DST
UTC Etc/UTC +00:00 No
Pacific America/Los_Angeles -08:00 (PST) Yes → -07:00 (PDT)
Japan Asia/Tokyo +09:00 (JST) No

JST never moves, so it is a stable anchor. Pacific is the column most likely to surprise you twice a year.

How to convert a Unix timestamp to PST and JST

You will not always be in a browser. Here are the same conversions in two other common environments, plus the offsets you should expect.

Using JavaScript Intl.DateTimeFormat

The browser snippet above is the canonical approach. The key facts: a Date is a UTC instant, multiply epoch seconds by 1000 to get milliseconds, and pass an IANA timeZone to Intl.DateTimeFormat. Add timeZoneName: "short" and the output literally prints PST, PDT, or JST, so a reader can confirm which offset was applied.

Using Python zoneinfo

Python's standard library ships IANA zone support through the zoneinfo module, so you do not need a third-party package.

from datetime import datetime, timezone
from zoneinfo import ZoneInfo

epoch = 1780949100
utc = datetime.fromtimestamp(epoch, tz=timezone.utc)

print(utc)
print(utc.astimezone(ZoneInfo("America/Los_Angeles")))
print(utc.astimezone(ZoneInfo("Asia/Tokyo")))

astimezone does the DST math for you based on the instant and the named zone.

Using the command line (date)

On Linux and macOS, date reads the TZ environment variable:

TZ=UTC date -d @1780949100
TZ=America/Los_Angeles date -d @1780949100
TZ=Asia/Tokyo date -d @1780949100

On macOS use date -r 1780949100 instead of -d @. The @ syntax says "this is an epoch value," which sidesteps any parsing ambiguity.

Why you should log in UTC with an ISO 8601 timestamp

The cleanest cross-zone comparison is the one you never have to do, because every system already logs the same way.

What RFC 3339 actually requires

RFC 3339 is the internet profile of ISO 8601. It uses T to separate date and time and Z to mark UTC, so 2026-06-08T20:05:00Z is unambiguous worldwide. RFC 3339 even distinguishes -00:00 (offset unknown, UTC preferred) from Z (definitely UTC) — a subtlety worth knowing when you parse third-party logs. Emit this format from every service and your "comparison" becomes a plain string sort.

IANA zone names vs fixed offsets

A fixed offset like -08:00 is a fact about one instant; an IANA name like America/Los_Angeles is a rule that knows the entire history of that region's offsets and DST transitions. The IANA Time Zone Database is updated whenever a government changes its rules, which is exactly why you store the region name and resolve the offset at display time rather than freezing a number.

Handling DST without losing your mind

DST creates two genuinely hard hours per year: a "spring forward" gap where a local time never happens, and a "fall back" hour that happens twice. Local timestamps can collide or vanish during those windows. UTC has neither problem because it never shifts. Log UTC, convert late, and the only zone that ever does DST math is the display layer.

Reading messy logs that already mix time zones

Sometimes you inherit logs you cannot fix at the source. Here is how to normalize them after the fact.

Spotting the offset in a raw line

Scan each timestamp for its trailing zone marker: Z, +09:00, -0700, or a name like JST. If a line has no offset at all, you have to know the producing system's zone out of band — that ambiguity is the single biggest reason to standardize on UTC. Treat an offset-less timestamp as a warning, not a value.

Extracting timestamps with regex

When timestamps are buried in free-form lines, pull them out with a pattern before converting. A Regex Tester lets you build and verify the pattern against real log samples first:

  • ISO-ish: \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\S*
  • Bracketed: \[(.*?)\]
  • Epoch in a field: \b1[0-9]{9}\b (10-digit seconds)

Capture the match, feed it to your converter, and key the line by the resulting epoch.

Diffing two log files after normalizing

Once both files are rewritten to UTC epochs, a line-by-line comparison finally means something. A Diff Checker will highlight where two normalized logs diverge, instead of showing every line as "different" just because one printed PST and the other JST. Normalize first, diff second.

References

Related on iKit

Related posts