Skip to content

fix: render admin timestamps in the viewer's timezone (#1041) - #1043

Merged
milkway merged 2 commits into
mainfrom
fix/1041-local-time-rendering
Jul 29, 2026
Merged

fix: render admin timestamps in the viewer's timezone (#1041)#1043
milkway merged 2 commits into
mainfrom
fix/1041-local-time-rendering

Conversation

@milkway

@milkway milkway commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Closes #1041.

O bug

Reportado no hugo: a aba Auditoria mostrava tudo 3 horas adiantado.

O banco guarda UTC (correto). O que faltava era conversão na renderização — chrono::DateTime::format imprime o relógio do próprio valor, então um DateTime<Utc> interpolado direto no template sai em UTC. Para quem está em UTC−3, o futuro.

Cinco telas tinham o mesmo defeito: Auditoria, Atividades dos usuários, Aplicações (atualizado em), Credenciais e Usuários. Nas colunas só-data o erro só aparecia entre 21h e meia-noite locais — por isso só a auditoria (com segundos, em eventos recentes) foi notada.

A correção

No cliente, não por configuração de fuso no servidor: box e hugo precisariam ser configurados um a um, e operadores podem estar em fusos diferentes de qualquer forma.

  • Cada célula vira <time datetime="…Z" data-rk-time="<padrão>">, com o texto UTC preservado como fallback sem JS.
  • Um renderizador no admin/_layout.html reescreve para o fuso do navegador. Os padrões espelham os formatos chrono que substituem (dd/mm/aaaa hh:mm:ss, dd/mm/aaaa hh:mm, dd/mm/aaaa, aaaa-mm-dd) — nenhuma tela muda de forma, só a hora fica certa.
  • Hover mostra a data completa com o nome do fuso, no idioma da página.
  • Sem strings i18n novas.

De brinde: o renderizador grava data-sort-value (epoch) na célula, consertando a ordenação da coluna de data — o enhancer de tabelas tirava os separadores e lia 29/07/2026 como um número só, ou seja, ordenava por dia do mês.

Verificação ao vivo

Instância local numa máquina em America/Recife (mesmo offset do hugo), Playwright dirigindo o Chrome instalado:

Tela padrão UTC no atributo renderizado
Auditoria datetime-s 2026-07-29T13:00:00Z 29/07/2026 10:00:00
Aplicações datetime 2026-07-29T13:00:00Z 29/07/2026 10:00
Usuários date-iso 2026-07-29T13:00:00Z 2026-07-29
Credenciais date 2026-07-30T01:00:00Z 29/07/2026

A última linha é o caso de virada de dia: o código antigo mostrava 30/07/2026, um dia inteiro errado. 46 células conferidas nas 5 telas, todas convertidas, nenhuma pulada.

Regressão

  • template_lint: um timestamp formatado sem o wrapper <time data-rk-time> quebra o teste.
  • template_lint: some o renderizador do layout, quebra também (o par attributo+script é o que faz a coisa funcionar).
  • activity_ui: a página renderizada tem que emitir um instante RFC-3339 de verdade no atributo, não o texto pt-BR.

Gate: cargo test verde, cargo clippy --all-targets -- -D warnings limpo, i18n-check OK.

Fora do escopo

Gotcha reconfirmada

Editar só um .html não rebuilda ruscker-admin — o smoke rodou contra o binário velho e o tooltip saiu no idioma errado até eu dar touch no lib.rs. O de sempre, registrado no CLAUDE.md.

🤖 Generated with Claude Code

milkway and others added 2 commits July 29, 2026 13:11
The audit log on hugo showed every event three hours in the future.
Not corrupt data — the DB stores UTC, which is right — but the
templates interpolated `DateTime<Utc>` straight into the page, and
chrono's `format` prints the wall clock OF THE VALUE. A UTC−3
operator therefore read UTC.

Five screens carried the same defect: audit, user activity, apps
(updated at), credentials and users. The date-only columns hid it
except between 21h and midnight local, which is why only the audit
log — seconds precision, recent events — got reported.

Fixed client-side rather than with a server timezone setting: box and
hugo would each need configuring, and operators can sit in different
zones anyway. Each cell now ships as
`<time datetime="…Z" data-rk-time="pattern">` with the UTC text kept
as the no-JS fallback, and a small renderer in the admin layout
rewrites it into the viewer's zone. The patterns mirror the chrono
formats they replace, so no screen changes shape — only the hour is
now right. Hovering shows the full date plus the zone name, in the
page's locale.

Bonus fix in the same pass: the renderer stamps `data-sort-value`
(epoch ms) on the cell, so the table enhancer sorts the date column by
instant. It used to strip the separators and read `29/07/2026` as one
number, i.e. sort by day-of-month.

Verified live against a local instance on an America/Recife machine
(the same offset as hugo) driving the installed Chrome: a seeded
13:00Z row renders 10:00:00, and a 2026-07-30T01:00:00Z credential
renders 29/07/2026 — the date-boundary case the old code got a full
day wrong. All 5 screens convert; 46 cells checked, none skipped.

Two lints keep it from regressing: a bare timestamp format in a
template without the `<time>` wrapper fails, and so does dropping the
renderer from the layout. Plus a render test asserting the activity
page emits a real RFC-3339 instant in the attribute.

The scheduler evaluating cron in UTC is the same gap with an
operational consequence, tracked separately in #1042.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reviewing my own PR turned up a sixth screen with the same defect,
which #1041's survey missed: it grepped for chrono `format` calls in
templates, and the Logs tab builds its timestamp in JavaScript.

`/admin/logs` takes the tracing stream's ISO timestamp, strips the
date and the trailing Z, and prints the remaining `HH:MM:SS.mmm` —
i.e. UTC, three hours ahead for a UTC−3 operator, exactly like the
audit table.

Now parsed into a Date and rendered in the viewer's zone, keeping the
millisecond precision the design asks for. The raw UTC token moves to
the cell's `title`, so correlating a line with the downloaded log file
(a server endpoint, still UTC — as a log file should be) stays
possible. An unparseable first field falls back to the old string
stripping rather than blanking the column.

Verified live: a line logged at 17:38:06.973Z renders 14:38:06.973
with the full UTC instant on hover.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@milkway

milkway commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Auto-review (autor = revisor, então com rigor extra)

Passei o diff procurando o que ele quebra. Três verificações e um achado real.

Achado real, corrigido em 57d8b47: a aba Logs tinha o mesmo bug e escapou do #1041. A varredura da issue procurou chamadas format de chrono nos templates — e o /admin/logs monta o timestamp em JavaScript: pega o instante ISO do stream do tracing, tira a data e o Z, e imprime o HH:MM:SS.mmm que sobra, ou seja, UTC. Três horas adiantado, igualzinho à auditoria. Agora converte para o fuso do viewer mantendo os milissegundos, com o instante UTC bruto no title da célula para quem for correlacionar com o log baixado (esse endpoint é do servidor e continua em UTC, como um arquivo de log deve ser). Campo não-parseável cai no comportamento antigo em vez de esvaziar a coluna. Verificado ao vivo: linha de 17:38:06.973Z renderiza 14:38:06.973.

Verificações que passaram:

  • Ordenação. O data-sort-value que eu gravo é lido pelo cellVal do enhancer e passa pelo parseFloat como número — ordena por instante. Na auditoria ele não faz nada (a tabela não tem data-enhance, de propósito: ordenar separaria a linha do seu diff), mas é inofensivo. Onde ele conta é em Aplicações, Usuários e Credenciais, que têm data-enhance.
  • Não sobrescrevo nada. Nenhum dos cinco templates emite data-sort-value no servidor.
  • Timestamps pré-formatados no Rust (schedules_ui.rs:114, routes/admin/users.rs:374, routes/admin/mfa.rs:198) continuam intactos e rotulados UTC — honestos, mas inconsistentes com estas telas. Alinhar junto do Agendador de cronjobs avalia expressões cron em UTC — jobs disparam fora do horário local esperado #1042, que é quem vai trazer um fuso do lado servidor.

Gate local: clippy -D warnings limpo, i18n OK. O cargo test teve uma falha ambiental, não do diff: ruscker-docker::readiness_wait_honours_configured_timeout estourou 240 s porque o Docker Desktop desta máquina travou (um docker version ficou pendurado >120 s). O teste é de readiness de container; o diff aqui é HTML e testes de ruscker-admin, que ruscker-docker não lê. O mesmo teste passou no CI deste PR e no cargo test completo de antes desta mudança.

@milkway
milkway marked this pull request as ready for review July 29, 2026 18:01
@milkway
milkway merged commit 5c35b8e into main Jul 29, 2026
5 checks passed
@milkway
milkway deleted the fix/1041-local-time-rendering branch July 29, 2026 18:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auditoria (e mais 4 telas) exibem timestamps em UTC — 3h adiantados no fuso do Brasil

1 participant