<div class="docs-content" data-render>
{% raw %}
# Timezone-aware dates

Every date and time shown on DevPlace is displayed in **your own timezone**, automatically. You do not set a timezone anywhere: the page works it out from your browser and converts every timestamp to your local time. Two people in different parts of the world looking at the same post see the same moment, each in their own clock.

## What you see

- **Relative times** such as `5m ago`, `3h ago`, or `2d ago` are shown for anything recent and update on their own while the page is open.
- After about a month, a relative time switches to an absolute **DD/MM/YYYY** date, shown in your local timezone.
- Hover over any time to see the **full local date and time** in a tooltip.

Because everything is anchored to a single universal instant, the conversion is exact: a message sent at 14:30 UTC shows as 15:30 for a reader in Central Europe and 09:30 for a reader on the US East Coast, with no configuration on either side.

## How it works

DevPlace stores and sends every timestamp as **UTC** (Coordinated Universal Time) in the standard ISO 8601 format. The server never guesses your timezone. Instead, each timestamp is delivered to your browser as a machine-readable value, and a small piece of JavaScript reformats it to your local timezone using the browser's built-in internationalization support. The browser already knows its own timezone, so this needs no cookie, no header, and no account setting.

If JavaScript is disabled, you still see a sensible fallback date (the server-rendered text inside the element), so the page is never blank. With JavaScript on, that text is replaced by your local time the moment the page loads, and any times added later (a new message arriving, more posts loading as you scroll) are localized automatically.

## For contributors

The mechanism is two Jinja globals plus one frontend module.

**Server side.** Use `local_dt(iso, mode)` or the relative-time shortcut `dt_ago(iso)` in templates. They emit a `<time>` element carrying the normalized UTC value and a server-rendered fallback:

```
<time datetime="2026-06-16T14:30:00+00:00" data-dt data-dt-mode="ago">3h ago</time>
```

`mode` is one of `date` (DD/MM/YYYY), `datetime` (DD/MM/YYYY HH:MM), or `ago` (relative). `local_dt` normalizes a naive timestamp to UTC, returns an empty string for empty input, and returns the raw value unchanged if it cannot be parsed.

**Client side.** `static/js/LocalTime.js` (reachable as `app.localTime`) finds every element marked `data-dt`, reformats its text to the browser's local timezone, and sets a full-datetime tooltip. A `MutationObserver` localizes any date inserted after load, and relative times refresh every 60 seconds. Frontend code that builds dates itself (for example the live message bubbles) creates the same `data-dt` element and the observer handles the rest.

When converting an existing display that used a precomputed relative string, keep a fallback so a missing field never blanks the date:

```
{{ dt_ago(item.created_at) if item.created_at else item.time_ago }}
```

**What stays as plain text.** The `format_date()` and `time_ago()` helpers remain plain-text functions for JSON API responses, the no-JavaScript fallback, and the few dates that must not be localized: user-entered calendar dates such as a project's release or demo date (which are plain DD/MM/YYYY values, not a specific instant), and any date placed inside an HTML attribute. Do not wrap those in `local_dt`.
{% endraw %}
</div>
