mirror of
https://github.com/R0m1k3/noteflow.git
synced 2026-10-11 17:29:37 +02:00
Merge pull request #101 from R0m1k3/claude/fix-time-offset-bug-01FzrcjKh9o5emsU6hRMMLRL
Fix: Correction du décalage horaire lors de l'affichage des événement…
This commit is contained in:
3 files changed
+268
-4
No files matched your search
+22
-4
@@ -3,12 +3,27 @@ const { Pool, types } = require('pg');
|
||||
const bcrypt = require('bcrypt');
|
||||
const logger = require('./logger');
|
||||
|
||||
// IMPORTANT: Désactiver le parsing automatique des TIMESTAMPTZ en objets Date
|
||||
// Pour éviter les problèmes de timezone, on renvoie les dates comme strings ISO
|
||||
// IMPORTANT: Parser personnalisé pour TIMESTAMPTZ
|
||||
// On force PostgreSQL à renvoyer en UTC (options: '-c timezone=UTC')
|
||||
// Mais PostgreSQL peut renvoyer "2024-11-17 09:20:00" sans le 'Z'
|
||||
// On doit normaliser en ISO UTC propre
|
||||
types.setTypeParser(1184, function(stringValue) {
|
||||
// 1184 = TIMESTAMPTZ
|
||||
// Renvoyer la string ISO au lieu d'un objet Date
|
||||
return stringValue;
|
||||
if (!stringValue) return null;
|
||||
|
||||
// PostgreSQL avec timezone=UTC renvoie: "2024-11-17 09:20:00" ou "2024-11-17 09:20:00+00"
|
||||
// On doit toujours renvoyer une ISO string UTC propre avec 'Z'
|
||||
|
||||
// Si déjà au format ISO avec Z ou timezone
|
||||
if (stringValue.includes('Z') || stringValue.match(/[+-]\d{2}:\d{2}$/)) {
|
||||
return new Date(stringValue).toISOString();
|
||||
}
|
||||
|
||||
// Si format "YYYY-MM-DD HH:MM:SS" sans timezone
|
||||
// Comme timezone=UTC, on sait que c'est en UTC
|
||||
// On ajoute 'Z' pour forcer JavaScript à l'interpréter comme UTC
|
||||
const isoString = stringValue.replace(' ', 'T') + 'Z';
|
||||
return new Date(isoString).toISOString();
|
||||
});
|
||||
|
||||
// Configuration PostgreSQL depuis DATABASE_URL ou variables d'environnement
|
||||
@@ -21,6 +36,9 @@ const pool = new Pool({
|
||||
max: 20, // Maximum de connexions dans le pool
|
||||
idleTimeoutMillis: 30000,
|
||||
connectionTimeoutMillis: 2000,
|
||||
// IMPORTANT: Forcer le timezone à UTC pour toutes les connexions
|
||||
// Cela garantit que PostgreSQL renvoie toujours les dates en UTC
|
||||
options: '-c timezone=UTC'
|
||||
});
|
||||
|
||||
// Gestion des erreurs du pool
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
// Script de debug pour comprendre le flux de synchronisation
|
||||
const { Pool } = require('pg');
|
||||
|
||||
const DATABASE_URL = process.env.DATABASE_URL ||
|
||||
`postgresql://${process.env.PGUSER || 'noteflow'}:${process.env.PGPASSWORD || 'noteflow_secure_password_change_me'}@${process.env.PGHOST || 'localhost'}:${process.env.PGPORT || '5499'}/${process.env.PGDATABASE || 'noteflow'}`;
|
||||
|
||||
const pool = new Pool({
|
||||
connectionString: DATABASE_URL
|
||||
});
|
||||
|
||||
async function debug() {
|
||||
console.log('═══════════════════════════════════════════════════════════');
|
||||
console.log('🔍 DEBUG SYNCHRONISATION CALENDRIER');
|
||||
console.log('═══════════════════════════════════════════════════════════\n');
|
||||
|
||||
try {
|
||||
// 1. Récupérer l'événement problématique
|
||||
const result = await pool.query(`
|
||||
SELECT
|
||||
title,
|
||||
start_time,
|
||||
pg_typeof(start_time) as type_colonne,
|
||||
start_time::text as start_text,
|
||||
to_char(start_time, 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') as start_iso_format,
|
||||
start_time AT TIME ZONE 'UTC' as start_utc,
|
||||
start_time AT TIME ZONE 'Europe/Paris' as start_paris
|
||||
FROM calendar_events
|
||||
WHERE title LIKE '%Wlodarczak%' OR title LIKE '%Julian%'
|
||||
ORDER BY start_time DESC
|
||||
LIMIT 1
|
||||
`);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
console.log('❌ Événement "Wlodarczak" non trouvé dans la base');
|
||||
console.log('');
|
||||
|
||||
// Afficher tous les événements
|
||||
const allEvents = await pool.query(`
|
||||
SELECT id, title, start_time
|
||||
FROM calendar_events
|
||||
ORDER BY start_time DESC
|
||||
LIMIT 5
|
||||
`);
|
||||
|
||||
console.log('Derniers événements dans la base:');
|
||||
allEvents.rows.forEach(e => {
|
||||
console.log(` - ${e.title}: ${e.start_time}`);
|
||||
});
|
||||
|
||||
await pool.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const event = result.rows[0];
|
||||
|
||||
console.log('1️⃣ ÉVÉNEMENT DANS POSTGRESQL:');
|
||||
console.log(' Titre:', event.title);
|
||||
console.log(' Type de colonne:', event.type_colonne);
|
||||
console.log(' start_time (brut):', event.start_time);
|
||||
console.log(' start_time::text:', event.start_text);
|
||||
console.log(' Format ISO:', event.start_iso_format);
|
||||
console.log(' AT TIME ZONE UTC:', event.start_utc);
|
||||
console.log(' AT TIME ZONE Europe/Paris:', event.start_paris);
|
||||
console.log('');
|
||||
|
||||
// 2. Simuler ce que reçoit le frontend
|
||||
console.log('2️⃣ CE QUE LE FRONTEND REÇOIT:');
|
||||
const apiResult = await pool.query(`
|
||||
SELECT id, title, start_time, end_time
|
||||
FROM calendar_events
|
||||
WHERE title LIKE '%Wlodarczak%' OR title LIKE '%Julian%'
|
||||
ORDER BY start_time DESC
|
||||
LIMIT 1
|
||||
`);
|
||||
|
||||
console.log(' JSON API:', JSON.stringify(apiResult.rows[0], null, 2));
|
||||
console.log(' Type de start_time:', typeof apiResult.rows[0].start_time);
|
||||
console.log('');
|
||||
|
||||
// 3. Simuler ce que fait le frontend
|
||||
console.log('3️⃣ SIMULATION FRONTEND:');
|
||||
const startTime = apiResult.rows[0].start_time;
|
||||
console.log(' start_time reçu:', startTime, '(type:', typeof startTime + ')');
|
||||
|
||||
const dateObj = new Date(startTime);
|
||||
console.log(' new Date(start_time):', dateObj);
|
||||
console.log(' .toISOString():', dateObj.toISOString());
|
||||
console.log(' .toString():', dateObj.toString());
|
||||
|
||||
const parisTime = dateObj.toLocaleTimeString('fr-FR', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
timeZone: 'Europe/Paris'
|
||||
});
|
||||
console.log(' .toLocaleTimeString(...Europe/Paris):', parisTime);
|
||||
console.log('');
|
||||
|
||||
// 4. Ce que devrait être l'heure
|
||||
console.log('4️⃣ VÉRIFICATION:');
|
||||
console.log(' Google Calendar dit: 10:20');
|
||||
console.log(' NoteFlow affiche:', parisTime);
|
||||
if (parisTime === '10:20') {
|
||||
console.log(' ✅ CORRECT');
|
||||
} else {
|
||||
console.log(' ❌ INCORRECT - Décalage de', Math.abs(10*60+20 - (parseInt(parisTime.split(':')[0])*60 + parseInt(parisTime.split(':')[1]))), 'minutes');
|
||||
}
|
||||
console.log('');
|
||||
|
||||
// 5. Test avec différentes conversions
|
||||
console.log('5️⃣ TESTS DE CONVERSION:');
|
||||
|
||||
// Test si c'est stocké en UTC
|
||||
if (typeof startTime === 'string') {
|
||||
console.log(' String PostgreSQL:', startTime);
|
||||
|
||||
// Si format "2024-11-17 09:20:00"
|
||||
if (!startTime.includes('T') && !startTime.includes('Z') && !startTime.includes('+')) {
|
||||
console.log(' ⚠️ Format sans timezone détecté!');
|
||||
console.log(' new Date() va interpréter comme heure LOCALE du serveur');
|
||||
|
||||
// Essayer de construire une date ISO UTC
|
||||
const isoUTC = startTime.replace(' ', 'T') + 'Z';
|
||||
console.log(' Tentative avec "Z": new Date("' + isoUTC + '")');
|
||||
const dateUTC = new Date(isoUTC);
|
||||
const parisUTC = dateUTC.toLocaleTimeString('fr-FR', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
timeZone: 'Europe/Paris'
|
||||
});
|
||||
console.log(' → Affichage Paris:', parisUTC);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('');
|
||||
console.log('═══════════════════════════════════════════════════════════');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur:', error.message);
|
||||
} finally {
|
||||
await pool.end();
|
||||
}
|
||||
}
|
||||
|
||||
debug();
|
||||
@@ -0,0 +1,102 @@
|
||||
// Test du parser PostgreSQL
|
||||
console.log('═══════════════════════════════════════════════════════════');
|
||||
console.log('🧪 TEST DU PARSER POSTGRESQL');
|
||||
console.log('═══════════════════════════════════════════════════════════\n');
|
||||
|
||||
// Simuler le parser
|
||||
function parseTimestampTZ(stringValue) {
|
||||
if (!stringValue) return null;
|
||||
|
||||
// PostgreSQL avec timezone=UTC renvoie: "2024-11-17 09:20:00" ou "2024-11-17 09:20:00+00"
|
||||
// On doit toujours renvoyer une ISO string UTC propre avec 'Z'
|
||||
|
||||
// Si déjà au format ISO avec Z ou timezone (+HH:MM ou +HH)
|
||||
if (stringValue.includes('Z') || stringValue.match(/[+-]\d{2}(:\d{2})?$/)) {
|
||||
return new Date(stringValue).toISOString();
|
||||
}
|
||||
|
||||
// Si format "YYYY-MM-DD HH:MM:SS" sans timezone
|
||||
// Comme timezone=UTC, on sait que c'est en UTC
|
||||
// On ajoute 'Z' pour forcer JavaScript à l'interpréter comme UTC
|
||||
const isoString = stringValue.replace(' ', 'T') + 'Z';
|
||||
return new Date(isoString).toISOString();
|
||||
}
|
||||
|
||||
// Test 1: Format PostgreSQL sans timezone (le plus courant)
|
||||
console.log('Test 1: PostgreSQL format sans timezone');
|
||||
const input1 = '2024-11-17 09:20:00';
|
||||
const output1 = parseTimestampTZ(input1);
|
||||
console.log(' Input:', input1);
|
||||
console.log(' Output:', output1);
|
||||
console.log(' ✓ Attendu: 2024-11-17T09:20:00.000Z');
|
||||
console.log(' ✓ Match:', output1 === '2024-11-17T09:20:00.000Z');
|
||||
console.log('');
|
||||
|
||||
// Test 2: Avec timezone +00
|
||||
console.log('Test 2: PostgreSQL avec timezone +00');
|
||||
const input2 = '2024-11-17 09:20:00+00';
|
||||
const output2 = parseTimestampTZ(input2);
|
||||
console.log(' Input:', input2);
|
||||
console.log(' Output:', output2);
|
||||
console.log(' ✓ Attendu: 2024-11-17T09:20:00.000Z');
|
||||
console.log('');
|
||||
|
||||
// Test 3: Déjà en ISO avec Z
|
||||
console.log('Test 3: Déjà en ISO UTC');
|
||||
const input3 = '2024-11-17T09:20:00Z';
|
||||
const output3 = parseTimestampTZ(input3);
|
||||
console.log(' Input:', input3);
|
||||
console.log(' Output:', output3);
|
||||
console.log(' ✓ Attendu: 2024-11-17T09:20:00.000Z');
|
||||
console.log('');
|
||||
|
||||
// Test 4: Affichage frontend
|
||||
console.log('Test 4: Affichage frontend avec toLocaleTimeString');
|
||||
const parsed = parseTimestampTZ('2024-11-17 09:20:00');
|
||||
const date = new Date(parsed);
|
||||
const parisTime = date.toLocaleTimeString('fr-FR', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
timeZone: 'Europe/Paris'
|
||||
});
|
||||
console.log(' Input PostgreSQL: 2024-11-17 09:20:00 (09:20 UTC)');
|
||||
console.log(' Après parser:', parsed);
|
||||
console.log(' new Date():', date);
|
||||
console.log(' Affiché en Paris:', parisTime);
|
||||
console.log(' ✓ Attendu: 10:20 (09:20 UTC = 10:20 Paris en hiver)');
|
||||
console.log(' ✓ Match:', parisTime === '10:20');
|
||||
console.log('');
|
||||
|
||||
// Test 5: Flux complet Google → PostgreSQL → Frontend
|
||||
console.log('Test 5: Flux complet Google Calendar → Frontend');
|
||||
console.log(' 1. Google Calendar envoie: "2024-11-17T10:20:00+01:00" (10:20 Paris)');
|
||||
|
||||
const googleDate = new Date('2024-11-17T10:20:00+01:00');
|
||||
console.log(' 2. Converti en UTC:', googleDate.toISOString(), '(09:20 UTC)');
|
||||
|
||||
const pgStored = '2024-11-17 09:20:00'; // Ce que PostgreSQL stocke en UTC
|
||||
console.log(' 3. PostgreSQL stocke:', pgStored, '(format sans Z)');
|
||||
|
||||
const pgParsed = parseTimestampTZ(pgStored);
|
||||
console.log(' 4. Parser renvoie:', pgParsed);
|
||||
|
||||
const frontendDate = new Date(pgParsed);
|
||||
const frontendDisplay = frontendDate.toLocaleTimeString('fr-FR', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
timeZone: 'Europe/Paris'
|
||||
});
|
||||
console.log(' 5. Frontend affiche:', frontendDisplay);
|
||||
|
||||
if (frontendDisplay === '10:20') {
|
||||
console.log(' ✅ SUCCÈS: L\'heure affichée correspond à Google Calendar!');
|
||||
} else {
|
||||
console.log(' ❌ ÉCHEC: Décalage détecté');
|
||||
console.log(' Attendu: 10:20');
|
||||
console.log(' Obtenu:', frontendDisplay);
|
||||
}
|
||||
console.log('');
|
||||
|
||||
console.log('═══════════════════════════════════════════════════════════');
|
||||
console.log('✅ Tests terminés');
|
||||
console.log('═══════════════════════════════════════════════════════════');
|
||||
Reference in new issue
Block a user