DEV Community

Cover image for Store Timestamps in UTC, Display Them Locally
chirag
chirag

Posted on

Store Timestamps in UTC, Display Them Locally

A CRM stores '10:30' beside a follow-up task. One representative reads it in Jaipur and another in London. Neither knows which timezone the value represents.

Time data needs to describe either an instant or a local scheduling intention. Those are different requirements.

Store instants unambiguously

An enquiry acceptance time is an instant. Store it using a timezone-aware database type or an explicit UTC representation supported by your system.

JavaScript can serialize a valid Date as a UTC ISO string:

const acceptedAt = new Date();
const timestamp = acceptedAt.toISOString();
Enter fullscreen mode Exit fullscreen mode

This records the browser or server clock's value. For authoritative acceptance time, prefer the backend's timestamp and maintain its clock synchronization.

Do not infer an instant from an ambiguous string such as 06/07/2026 10:30.

Format for the reader

Display an instant in an explicit timezone using a locale-aware formatter:

const display = new Intl.DateTimeFormat("en-GB", {
  dateStyle: "medium",
  timeStyle: "short",
  timeZone: "Asia/Kolkata"
}).format(acceptedAt);
Enter fullscreen mode Exit fullscreen mode

Choose the display timezone from a documented user or application setting. Label it where confusion is possible.

Preserve scheduling intent

'Call tomorrow at 9 AM in the customer's timezone' contains more than an instant. Store the selected local date and time together with an IANA timezone identifier, then resolve it for execution.

Recurring local schedules need their timezone rules preserved. A fixed UTC offset does not represent future daylight-saving changes.

Some local times occur twice when clocks move backward; others do not occur when clocks move forward. Ask for or document a resolution policy rather than silently choosing an arbitrary instant.

Keep audit fields separate

Field Meaning
Accepted at Backend accepted the enquiry
Event occurred at Source reports when the event happened
Received at Integration received the event
Next attempt at Planned operational retry

A delayed event's receipt time is not its occurrence time. Avoid overwriting one with another.

Test meaningful boundaries

Test different viewer timezones, month and year boundaries, daylight-saving transitions for relevant regions, invalid dates, and absent timezone settings.

The conversion code above is illustrative and should be verified in your target runtime. Consistent timestamps make routing, follow-ups, and incident investigation easier because everyone can identify which moment a record describes.

Top comments (1)

Collapse
 
onelifevibe profile image
OneLifeVibe •

The distinction between an instant and a scheduling intention is the part I wish more write-ups made this clearly. Storing "call tomorrow at 9 AM in the customer's timezone" as a plain UTC timestamp quietly throws away the intent, and it only shows up months later when the clocks change. Keeping the audit fields separate — received at versus event occurred at versus next attempt at — is also a genuinely useful habit; I have seen delayed events overwrite the original time and then nobody can reconstruct what actually happened. The testing checklist at the end is a good reminder too, especially the DST transitions and month boundaries.