fix(timezone): enforce BRT explicitly on capture to prevent server UTC offset and restore TO_CHAR constraint

This commit is contained in:
Sidney 2026-05-25 11:10:49 -03:00
parent 6cdf609f62
commit 3e5c9afda2
3 changed files with 14 additions and 8 deletions

View File

@ -71,4 +71,6 @@
42. **Cronological Display Standard**: Consultas a cadastros secundários (como Disciplinas e Categorias) DEVEM utilizar `ORDER BY created_at ASC` no SQL para manter a mesma ordem de listagem original do sistema (evitando reordenação alfabética não-intencional).
43. **Mass Contracts Update**: A edição de um "Modelo de Contrato" dispara atualizações em massa (PUT `/api/contratos/:id`) recalcularizando as variáveis de todos os contratos já emitidos para os alunos vinculados àquele modelo, garantindo atualização em tempo real no Portal do Aluno via SQL.
44. **Biometria SQL-First Parcial**: A biometria (faceDescriptor) já está preparada para persistência nativa na coluna jsonb 'face_descriptor' do PostgreSQL (com mapeamento camelCase na API). Contudo, o cadastro de alunos via Manager ainda não utiliza a API SQL em sua plenitude no handleSave. Até a refatoração do Students.tsx, os alunos novos continuarão dependendo do fallback do school_data.json.
45. **Forçar Fuso Horário Brasileiro na Origem**: Ao gerar datas ou horários locais no frontend para envio ao banco PostgreSQL (como registros de biometria ou ponto), é OBRIGATÓRIO forçar o cálculo no fuso 'America/Sao_Paulo' (ex: \
ew Date().toLocaleString('en-US', { timeZone: 'America/Sao_Paulo' })\). Isso evita que instâncias rodando em UTC no Windows Server ou celulares com fuso errado enviem horários defasados, o que causaria quebra em lógicas de restrição de janela de tempo (ex: horário de aula).

View File

@ -252,13 +252,17 @@ const AttendanceCapture: React.FC<AttendanceCaptureProps> = ({ data, updateData
return;
}
// Gerar string de data local para o banco de dados
const localDateStr = nowLocal.getFullYear() + '-' +
String(nowLocal.getMonth() + 1).padStart(2, '0') + '-' +
String(nowLocal.getDate()).padStart(2, '0') + 'T' +
String(nowLocal.getHours()).padStart(2, '0') + ':' +
String(nowLocal.getMinutes()).padStart(2, '0') + ':' +
String(nowLocal.getSeconds()).padStart(2, '0');
// Gerar string de data local forçando o fuso horário de Brasília (America/Sao_Paulo)
// Isso evita que dispositivos em UTC (como o servidor Windows) salvem horários +3 horas
const brtString = new Date().toLocaleString('en-US', { timeZone: 'America/Sao_Paulo' });
const brtDate = new Date(brtString);
const localDateStr = brtDate.getFullYear() + '-' +
String(brtDate.getMonth() + 1).padStart(2, '0') + '-' +
String(brtDate.getDate()).padStart(2, '0') + 'T' +
String(brtDate.getHours()).padStart(2, '0') + ':' +
String(brtDate.getMinutes()).padStart(2, '0') + ':' +
String(brtDate.getSeconds()).padStart(2, '0');
// Limpar qualquer falta auto-gerada para o mesmo aluno nesta mesma aula/dia
const filteredAttendance = (data.attendance || []).filter(a => {

View File

@ -835,7 +835,7 @@ export async function getFrequencias() {
classId: r.turma_id,
lessonId: r.aula_id,
date: r.formatted_data || r.data,
photo: r.foto,
photo: r.foto_url || r.foto,
verified: r.verificado,
type: r.tipo,
justification: r.justificativa,