Client-side timezone annotation versus server-side UTC normalization for Sentry event timestamps
0 reputation · 01 Sept 2021, 09:14 UTC
0 reputation · 01 Sept 2021, 09:14 UTC
Sentry SDKs capture event timestamps in UTC by default (ISO 8601/RFC 3339), and the server-side processing pipeline normalizes all incoming timestamps to UTC before storage. However, the server assumes all timestamps arrive in UTC unless explicitly marked otherwise. This creates a design decision for distributed systems operating across multiple time zones.
Attach explicit timezone offsets or IANA timezone identifiers to each event payload before sending. This preserves the original regional context for debugging and correlation with local logs, but requires custom integration work since standard SDKs don't expose timezone annotation fields in the public API.
Send only UTC timestamps from all clients and perform timezone conversion at query or display time in the Sentry UI. This avoids custom payload modifications and works with standard SDK behavior, but loses the original local-time context that can be critical for reproducing time-sensitive issues like cron jobs or business-hour alerts.
Both approaches are documented as viable in Sentry's event payload specification, but the SDKs provide no built-in mechanism for Approach A, and Approach B's display-time conversion depends on UI settings that may not be consistent across team members.
34775 reputation · 01 Sept 2021, 17:23 UTC
Use Approach B for the timestamp itself (UTC, exactly as the SDKs already send it) and borrow a small piece of Approach A: attach the originating IANA timezone as event context or a tag. Do not put local-time or offset-bearing values into the event timestamp field. This hybrid gives you DST-immune, correctly ordered events plus the regional context you need to correlate with cron schedules and local logs.
For services spanning regions with different DST rules, UTC is the only reliable baseline. Local timestamps with fixed offsets are ambiguous around DST transitions — the same local wall-clock time can occur twice in autumn, and offset arithmetic done after the fact can't retroactively tell you which occurrence an event belonged to. Sentry's ingestion pipeline normalizes everything to UTC before storage, so events arrive monotonic and comparable regardless of origin region. Putting anything nonstandard in the timestamp field risks ingestion problems or misordered events — this is the one place you should not get creative.
The genuine gap in UTC-only is human context: a UTC timestamp alone doesn't tell you whether an error fired during Tokyo business hours or five minutes after a Berlin cron job. You recover that without touching the timestamp by attaching the timezone as metadata. Every major Sentry SDK exposes hooks for this — typically beforeSend, setContext, or setTag (exact names vary by SDK and version, so check yours). For example:
Sentry.setContext("runtime", {
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
});
// or as a searchable tag:
Sentry.setTag("timezone", "America/New_York");Prefer IANA names (America/New_York) over fixed offsets (-05:00): the name encodes the DST rules, so anyone can reconstruct the local time at the moment of the event, even retroactively.
Full Approach A — modifying the timestamp payload itself — yes, largely. There is no standard timezone field in the event payload, so you'd be maintaining custom serialization against SDK internals for marginal benefit. But the metadata-only version above is a few lines in your existing SDK init, needs no ongoing maintenance, and survives SDK upgrades because it uses stable public hooks. That makes the "impractical" objection mostly moot.
Sentry's UI converts timestamps per each user's locale settings, so two teammates can see different local times for the same event in screenshots and shared views. Standardize on quoting UTC in incident timelines and runbooks; use the timezone tag only to reconstruct local context when reproducing time-sensitive issues.
If the timezone tag is missing from the raw JSON, the hook name or scope configuration in your SDK version is the first thing to check.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.