feat: times in the reader's own zone; the audit log shows exact moments (milestone 490)
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 1m0s
CI / unit (push) Failing after 1m23s
CI / publish (push) Skipped

- users.time_zone (0034). An account without one takes the browser's zone
  on its first page (POST /account/time-zone/adopt, never over a choice made
  by hand); the session follows the account on every request.
- /account: who you are and the zone every page writes its times in,
  reached from your name in the sidebar.
- formatting.when: 'Today 06:58', 'Yesterday 23:41', 'Tue 6 Oct 14:12',
  '6 Oct 2025 14:12', 24-hour, in the reader's zone; |ago's hover is the
  exact local moment instead of UTC; tz_label() for column headers.
- Audit log: When (EDT) with exact times, one detail fact a line, on classes.
- tzdata in the image, and the airgapped smoke checks a zone resolves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 07:56:27 -04:00
co-authored by Claude Opus 5.5
parent 955450d33f
commit 6a81cf4aac
19 changed files with 425 additions and 25 deletions
+21 -1
View File
@@ -68,8 +68,9 @@
{% endif %}
</nav>
<div class="side-user">
<a href="/account/" title="Your account and time zone"{% if _p.startswith('/account') %} aria-current="page"{% endif %}>{{ session.username }}</a>
<form method="post" action="/logout">{{ csrf_field() }}
<button type="submit" title="Log out">⏻ {{ session.username }}</button>
<button type="submit" title="Log out" aria-label="Log out">⏻</button>
</form>
</div>
</aside>
@@ -106,6 +107,25 @@ document.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && document.body.classList.contains('nav-open')) setNavOpen(false);
});
</script>
{% if session.user_id and not session.tz %}
<script>
// An account with no time zone yet takes this browser's, once (milestone 490);
// the page then reloads so its times are written in it.
(function () {
var tz;
try { tz = Intl.DateTimeFormat().resolvedOptions().timeZone; } catch (e) { return; }
if (!tz) return;
fetch('/account/time-zone/adopt', {
method: 'POST',
headers: {'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content},
body: JSON.stringify({time_zone: tz})
}).then(function (r) { return r.ok ? r.json() : null; })
.then(function (d) { if (d && d.adopted) location.reload(); })
.catch(function () {});
})();
</script>
{% endif %}
<main{% block main_class %}{% endblock %}>
{% if plugin_failures and session.user_role == 'admin' %}