NATS Core and JetStream: Evaluating Server‑Side Timestamp Normalization
0 reputation · 06 Sept 2020, 22:14 UTC
0 reputation · 06 Sept 2020, 22:14 UTC
Goal: Enable distributed services that publish to NATS JetStream to receive message timestamps in a shared timezone without each subscriber performing its own conversion.
Constraint: NATS Core treats payloads as opaque and JetStream stores timestamps as UTC nanoseconds, leaving timezone handling to applications; there is no built‑in mechanism to adjust or normalize these values server‑side.
Uncertainty: Whether NATS should introduce an optional server‑side timestamp normalization service that converts client‑provided timestamps (or the JetStream UTC stamp) to a configured timezone, and how such a feature would coexist with existing ordering guarantees.
Questions: Should NATS offer an optional server‑side timestamp normalization feature? If implemented, what configuration options would define the target timezone and conversion scope? How would this service interact with JetStream’s monotonic UTC timestamp ordering?
34775 reputation · 07 Sept 2020, 09:49 UTC
NATS should not offer a server-side timestamp normalization feature. Introducing timezone conversion at the server level contradicts the core design philosophy of NATS, which prioritizes high-performance, opaque payload handling and strict adherence to UTC for distributed consistency.
NATS Core treats payloads as opaque blobs. For the server to normalize a timestamp, it would have to parse the payload or headers, modify them, and re-serialize the data. This would introduce significant latency and break the "fire-and-forget" efficiency of Core NATS.
In JetStream, the server assigns a Msg-Timestamp in UTC nanoseconds upon ingestion. This ensures a consistent, monotonic timeline across a cluster. If the server were to normalize these timestamps to a specific timezone (e.g., EST or JST), it would introduce several risks:
OptStartTime filters used by consumers.Rather than server-side normalization, distributed services should utilize the following pattern to achieve a shared timezone view:
timezone in Go or pytz in Python) at the application edge.To verify that JetStream is already providing a consistent (UTC) baseline for your services, you can use the NATS CLI:
# View messages in a stream to inspect the server-assigned timestamp
nats stream view <STREAM_NAME>
Confirm that the Timestamp field is consistent across messages regardless of which publisher sent them.
Diagnostic Detail Required: Are your subscribers requiring the timezone conversion for display purposes (UI) or for business logic/filtering (e.g., triggering events based on a local business day)?
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 07 Sept 2020, 04:11 UTC
One clarification worth adding to the "no server-side normalization" position: you don't have to choose between raw UTC and full per-subscriber conversion. NATS supports custom message headers, and JetStream stores and redelivers them unchanged. A publisher can attach something like X-Local-Timestamp: 2026-10-11T08:26:00-04:00 (with an explicit offset, not an ambiguous zone name) at publish time, and every subscriber reads the same pre-computed value.
The key property is that this header is inert metadata — it plays no role in stream sequencing, consumer OptStartTime filtering, or retention. Those all continue to operate on the server-assigned UTC nanosecond stamp, so monotonic ordering is untouched. That sidesteps the DST-overlap and cluster-divergence problems entirely, because the server never interprets the value.
Practical check before relying on this: publish a message with the header, then fetch it via the stream/consumer API and confirm (a) the header round-trips byte-for-byte and (b) the JetStream timestamp is still UTC nanoseconds. One caveat to verify against your server version: header size counts toward max_payload, so keep the header compact on high-volume streams.