- Novo runbook KB-PLUGIN-031: nascimento do plugin ate validacao E2E no GLPI dev (scaffold, Forgejo local, deploy CT100, console, bootstrap Kernel para testes CLI). Validado de ponta a ponta com o assetinherit. - Registros KB-PLUGIN-021..030 existiam apenas no disco (drift) e foram incluidos no versionamento; index.json sincronizado via kb-fix. - Ignora lixo AppleDouble/.DS_Store do macOS. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5.2 KiB
| id | title | domain | tags | status | severity | created_at | updated_at | applies_to | related_records | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| KB-PLUGIN-025 | mcprotocol — Comportamento de timezone em datas sem hora explícita | plugin-dev |
|
active | low | 2026-05-26 | 2026-05-26 |
|
|
mcprotocol — Comportamento de timezone em datas sem hora explícita
Resumo
InputValidation::normalizeDate aceita YYYY-MM-DD e expande para YYYY-MM-DD 00:00:00 sem informação de timezone. O PHP do container GLPI está em date.timezone=UTC, então o valor enviado ao MariaDB é tratado como UTC e gravado como tal em colunas timestamp.
Para usuários sem TZ pessoal configurado (caso padrão do GLPI), esse comportamento é transparente — a UI exibe a data exatamente como recebida. Para usuários com TZ regional configurado (ex: America/Sao_Paulo), a data aparece deslocada nas horas correspondentes (-03:00 → "24/05 21:00" em vez de "25/05 00:00").
Detalhamento técnico
Cadeia de TZ no ambiente atual:
| Camada | TZ |
|---|---|
Container app (glpi11-app) — clock |
-03 (São Paulo) |
PHP (date.timezone) |
UTC |
Container MariaDB (glpi11-mariadb) — clock |
-03 |
MariaDB session (@@session.time_zone) |
SYSTEM (= -03) |
GLPI config (glpi_configs.timezone) |
0 (UTC default) |
Usuário glpi.timezone |
NULL (sem override) |
Quando normalizeDate('2026-05-25') retorna 2026-05-25 00:00:00:
- PHP envia a string para o ORM do GLPI
- ORM passa adiante para MariaDB
- MariaDB com sessão em
SYSTEMinterpretaria a string como horário local — mas o GLPI executaSET time_zone='+00:00'ao conectar (verificado emGlpi\System\Diagnostic), então a sessão fica UTC - Valor gravado:
2026-05-25 00:00:00 UTC✅
Confirmado por probe:
SET @@session.time_zone='+00:00';
SELECT plan_start_date FROM glpi_projecttasks WHERE id=33;
-- 2026-05-25 00:00:00 ← correto
REST API v2 retorna com sufixo explícito:
"plan_start_date": "2026-05-25T00:00:00+00:00"
Gotcha — Usuários com TZ regional
Cenário problemático:
- Admin cria usuário "joao" e seta
joao.timezone = 'America/Sao_Paulo'no perfil - LLM cria tarefa via plugin:
plan_start_date: "2026-05-25" - Banco grava:
2026-05-25 00:00:00 UTC - UI do João renderiza convertendo pra SP:
24/05/2026 21:00:00
O João vê dia 24 em vez de dia 25. Confusão garantida.
Mitigações
Curto prazo — documentação
Adicionar nota na descrição do schema dos campos plan_start_date/plan_end_date:
"Datas sem hora são interpretadas como 00:00 UTC. Para usuários em fusos regionais, recomenda-se enviar com hora explícita no fuso de interesse, ex: '2026-05-25 03:00:00' para meia-noite em São Paulo (UTC-3)."
Médio prazo — normalização inteligente
Estender InputValidation::normalizeDate para aceitar um TZ contextual e, quando data vier sem hora, expandir como meia-noite naquele TZ convertido para UTC:
public static function normalizeDate(
string $value,
string $field,
array &$errors,
?string $tz = null // novo parâmetro opcional
): ?string {
$tz = $tz ?? self::resolveContextTz();
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
$dt = new \DateTimeImmutable("$value 00:00:00", new \DateTimeZone($tz));
return $dt->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
}
// ... resto igual
}
private static function resolveContextTz(): string {
// Ordem: user preference → glpi config → UTC
$userTz = $_SESSION['glpi_tz'] ?? null;
if ($userTz) return $userTz;
global $CFG_GLPI;
return $CFG_GLPI['timezone'] ?? 'UTC';
}
Assim a data "2026-05-25" enviada por um LLM operando em nome de um usuário com TZ=SP resultaria em 2026-05-25 03:00:00 UTC, que renderiza corretamente como 25/05/2026 00:00 em SP.
Longo prazo — schema MCP com TZ embutido
Para datas com hora, padronizar input em ISO 8601 com TZ explícito:
"2026-05-25T00:00:00-03:00" ou "2026-05-25T00:00:00Z".
normalizeDate aceitaria também esse formato e converteria para UTC.
Diagnóstico de campo
Comando para auditar o estado real vs. UI:
# 1. Confirma TZ do user atual
SELECT name, timezone FROM glpi_users WHERE name = 'X';
# 2. Lê data forçando UTC (valor real armazenado)
SET @@session.time_zone='+00:00';
SELECT plan_start_date FROM <tabela> WHERE id=N;
# 3. Lê data no TZ do user (o que a UI vai mostrar)
SET @@session.time_zone='America/Sao_Paulo';
SELECT plan_start_date FROM <tabela> WHERE id=N;
Status
- Comportamento atual: correto para o caso padrão (sem TZ regional)
- Não há bug — é uma gap de UX prevista da extensão de schema
- Mitigação curto prazo: documentação no schema
- Mitigação ideal: TZ contextual no
normalizeDate(backlog do plugin)
Lições
timestampno MariaDB sempre armazena em UTC, independente da sessão- Strings de data sem TZ explícito dependem do
@@session.time_zonepara interpretação - GLPI já normaliza a sessão para UTC ao conectar — bom comportamento
- Mas o input do plugin precisa de TZ contextual para ser semanticamente correto