Skip to content

fix(logger): render log timestamps in local time instead of UTC - #1311

Draft
MathurAditya724 wants to merge 1 commit into
mainfrom
fix/log-timestamp-timezone-fallback
Draft

fix(logger): render log timestamps in local time instead of UTC#1311
MathurAditya724 wants to merge 1 commit into
mainfrom
fix/log-timestamp-timezone-fallback

Conversation

@MathurAditya724

Copy link
Copy Markdown
Member

Log timestamps (the [auth.login 6:28:59 AM] prefix) and other Date output were rendered in UTC instead of the user's local time. The CLI ships as Node SEA binaries that often can't resolve the OS timezone and silently fall back to UTC, so a user in US/Pacific saw timestamps hours off from their clock even with correct OS settings.

The fix detects the OS timezone at startup and sets process.env.TZ when the runtime fell back to UTC, and overrides consola's reporter formatDate to derive local time from getTimezoneOffset() in an unambiguous 24-hour format (robust even if ICU state is stale).

Testing

  • vitest run test/lib/timezone.test.ts test/lib/logger.test.ts test/lib/formatters — 975 passing (14 new timezone tests).
  • Manual: with TZ unset in a UTC-fallback runtime, verified the log prefix and toLocaleTimeString() render correct local time once repaired; confirmed initTimezone() is a no-op when TZ is set or the machine is genuinely UTC.
  • biome check clean on changed files; tsc clean on changed files (pre-existing generated-code errors unrelated).

The `[tag 6:28:59 AM]` log prefix (and every other Date display) is
rendered by the runtime's notion of "local" time, which comes from ICU.
The CLI ships as Node SEA binaries that frequently cannot resolve the OS
timezone and silently fall back to UTC, so users saw timestamps hours off
from their local clock even with correct OS settings.

- Add `src/lib/timezone.ts`: at startup, detect the OS timezone (from
  /etc/timezone, the /etc/localtime symlink, or `tzutil` on Windows) and
  set `process.env.TZ` when the runtime fell back to UTC but the OS clock
  is elsewhere. Falls back to a fixed `Etc/GMT±N` offset zone when no IANA
  name is available. No-op when TZ is already set or the runtime resolved
  a real zone.
- Override consola's reporter `formatDate` so the prefix derives local
  time from `getTimezoneOffset()` (robust against stale ICU state) and
  uses an unambiguous 24-hour `HH:MM:SS` format.
- Wire `initTimezone()` into `startCli()` before any Date is formatted.
@github-actions

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://cli.sentry.dev/_preview/pr-1311/

Built to branch gh-pages at 2026-07-28 15:38 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@github-actions

Copy link
Copy Markdown
Contributor

Codecov Results 📊

❌ Patch coverage is 70.18%. Project has 5504 uncovered lines.
❌ Project coverage is 81.55%. Comparing base (base) to head (head).

Files with missing lines (3)
File Patch % Lines
src/lib/timezone.ts 68.63% ⚠️ 16 Missing and 5 partials
src/lib/logger.ts 100.00% ⚠️ 2 partials
src/cli.ts 0.00% ⚠️ 1 Missing
Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
- Coverage    81.58%    81.55%    -0.03%
==========================================
  Files          428       429        +1
  Lines        29774     29831       +57
  Branches     19511     19549       +38
==========================================
+ Hits         24289     24327       +38
- Misses        5485      5504       +19
- Partials      2032      2039        +7

Generated by Codecov Action

@MathurAditya724 MathurAditya724 added the jared Trigger the Jared agent to work on stuff label Jul 28, 2026

@jared-outpost jared-outpost Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

took a pass through this — the approach is sound and CI is green. one doc nit:

formatLogTime JSDoc overstates ICU-independence. it says it derives local time "robust even if ICU state is stale" / "reflects the OS offset even in runtimes that cache a stale UTC formatter." but formatLogTime reads getTimezoneOffset(), which shares the exact same tz resolution as toLocaleTimeString() — in the broken UTC-fallback state getTimezoneOffset() returns 0 (that's the premise osReportsNonUtc() relies on), so formatLogTime would render UTC too. its correctness actually depends on initTimezone() having already set TZ so Node re-resolves the offset. worth rewording the comment so a future maintainer doesn't assume the function stands alone — the two fixes are coupled, not independent.

everything else checks out:

  • initTimezone() decision ladder (TZ-set → real-zone → genuine-UTC → repair) is right, and setting process.env.TZ at runtime does make Node re-resolve getTimezoneOffset() (verified).
  • Etc/GMT±N sign convention matches getTimezoneOffset() semantics (positive = west of UTC), and the offset-shift math in formatLogTime is correct.
  • reporter formatDate override preserves the rest of consola's formatting.
  • initTimezone() sits at the top of startCli(), the sole entry path, so nothing formats a Date before repair.

one heads-up, not a blocker: switching [tag 6:28:59 AM] → 24-hour [tag 06:28:59] is a format change beyond the timezone fix. it's disclosed in the description and the unambiguity is nice, just flagging it's a visible behavior change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

jared Trigger the Jared agent to work on stuff

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant