Files
noteflow/scripts/full-diagnosis-timezone.js
T
Claude d944884fe4 Fix: Timezone Europe/Paris + diagnostic complet calendrier
PROBLÈME PERSISTANT: Le décalage d'1h persiste malgré les fixes.

ANALYSE COMPLÈTE:
Le types.setTypeParser doit être appliqué AVANT le Pool,
ET l'image Docker doit être rebuild (pas juste restart),
ET les anciens événements doivent être supprimés et resynchronisés.

AJOUTS:

1. scripts/full-diagnosis-timezone.js
   - Diagnostic complet du problème
   - Test du type parser (Date object vs string)
   - Test insertion/lecture PostgreSQL
   - Vérification événements réels
   - Simulation API response
   - Recommandations claires selon le résultat

2. FIX-TIMEZONE-FINAL.md
   - Procédure COMPLÈTE étape par étape
   - REBUILD obligatoire avec --no-cache
   - Suppression événements corrompus
   - Resynchronisation Google Calendar
   - Vérifications multiples
   - Dépannage si problème persiste
   - Checklist finale

UTILISATION:

1. Diagnostic:
   docker exec notes-todo-app node scripts/full-diagnosis-timezone.js

2. Si parser KO (renvoie Date objects):
   docker-compose down
   docker-compose build --no-cache notes-app
   docker-compose up -d

3. Suppression anciens événements:
   docker exec noteflow-postgres psql -U noteflow -d noteflow -p 5499 \
     -c "DELETE FROM calendar_events"

4. Resynchroniser depuis interface web

5. Vérifier que heures = Google Calendar

Le diagnostic permettra d'identifier si:
- Le parser n'est pas actif → rebuild nécessaire
- Le parser fonctionne mais anciennes données → suppression nécessaire
- Autre problème → affichage exact du problème

Fichiers:
- scripts/full-diagnosis-timezone.js (nouveau)
- FIX-TIMEZONE-FINAL.md (guide complet)
2025-11-14 18:10:10 +00:00

172 lines
7.3 KiB
JavaScript
Executable File

#!/usr/bin/env node
// DIAGNOSTIC COMPLET - Traçage du décalage horaire
const { Pool } = require('pg');
const DATABASE_URL = process.env.DATABASE_URL;
async function fullDiagnosis() {
const pool = new Pool({ connectionString: DATABASE_URL });
try {
console.log('\n╔══════════════════════════════════════════════════════════════╗');
console.log('║ DIAGNOSTIC COMPLET - DÉCALAGE HORAIRE CALENDRIER ║');
console.log('╚══════════════════════════════════════════════════════════════╝\n');
const client = await pool.connect();
// 1. Vérifier la configuration du driver pg
console.log('1️⃣ CONFIGURATION DRIVER PG:\n');
const types = require('pg').types;
const parser1184 = types.getTypeParser(1184);
console.log(` Type parser 1184 (TIMESTAMPTZ): ${typeof parser1184}`);
console.log(` Parser renvoie: ${parser1184.name || 'function'}\n`);
// 2. Test direct avec une date connue
console.log('2️⃣ TEST INSERTION/LECTURE DIRECTE:\n');
await client.query('DELETE FROM calendar_events WHERE google_event_id LIKE $1', ['diagnostic-%']);
const testDates = [
{ name: 'Paris 10h avec offset', value: '2025-11-14T10:00:00+01:00' },
{ name: 'UTC 09h', value: '2025-11-14T09:00:00Z' },
{ name: 'Paris 14h avec offset', value: '2025-11-14T14:30:00+01:00' }
];
for (const test of testDates) {
const eventId = `diagnostic-${test.name.replace(/\s+/g, '-')}`;
await client.query(`
INSERT INTO calendar_events
(google_event_id, user_id, title, start_time, end_time, all_day)
VALUES ($1, 1, $2, $3, $4, FALSE)
`, [eventId, `Test ${test.name}`, test.value, test.value]);
const result = await client.query(`
SELECT
title,
start_time,
pg_typeof(start_time) as pg_type,
start_time::text as text_repr
FROM calendar_events
WHERE google_event_id = $1
`, [eventId]);
const row = result.rows[0];
console.log(` Test: ${test.name}`);
console.log(` Inséré: ${test.value}`);
console.log(` Type PG: ${row.pg_type}`);
console.log(` Texte: ${row.text_repr}`);
console.log(` JS type: ${typeof row.start_time}`);
console.log(` JS value: ${row.start_time}`);
if (typeof row.start_time === 'object' && row.start_time instanceof Date) {
console.log(` ❌ PROBLÈME: C'est un objet Date! Le parser n'a pas fonctionné!`);
console.log(` toISOString(): ${row.start_time.toISOString()}`);
} else if (typeof row.start_time === 'string') {
console.log(` ✅ OK: C'est une string!`);
const parsed = new Date(row.start_time);
console.log(` new Date(): ${parsed.toISOString()}`);
console.log(` Heure locale: ${parsed.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' })}`);
console.log(` Heure Paris: ${parsed.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Paris' })}`);
}
console.log('');
}
// 3. Vérifier événements réels
console.log('3️⃣ ÉVÉNEMENTS RÉELS GOOGLE CALENDAR:\n');
const realEvents = await client.query(`
SELECT
title,
start_time,
pg_typeof(start_time) as pg_type,
start_time::text as text_repr,
all_day
FROM calendar_events
WHERE google_event_id NOT LIKE 'diagnostic-%'
ORDER BY start_time ASC
LIMIT 3
`);
if (realEvents.rows.length === 0) {
console.log(' ⚠️ Aucun événement réel trouvé');
console.log(' → Synchronisez Google Calendar d\'abord\n');
} else {
realEvents.rows.forEach((evt, i) => {
console.log(` Événement ${i + 1}: ${evt.title}`);
console.log(` Type PG: ${evt.pg_type}`);
console.log(` Texte: ${evt.text_repr}`);
console.log(` JS type: ${typeof evt.start_time}`);
console.log(` JS value: ${evt.start_time}`);
if (typeof evt.start_time === 'object' && evt.start_time instanceof Date) {
console.log(` ❌ Objet Date (parser KO)`);
console.log(` Affiché frontend: ${evt.start_time.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Paris' })}`);
} else {
console.log(` ✅ String (parser OK)`);
const d = new Date(evt.start_time);
console.log(` Affiché frontend: ${d.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Paris' })}`);
}
console.log('');
});
}
// 4. Test de l'API
console.log('4️⃣ SIMULATION API RESPONSE:\n');
const apiResult = await client.query(`
SELECT
id, google_event_id, title, description,
start_time, end_time, location, html_link, all_day
FROM calendar_events
WHERE google_event_id LIKE 'diagnostic-%'
LIMIT 1
`);
if (apiResult.rows.length > 0) {
const apiEvent = apiResult.rows[0];
console.log(' API renvoie (format JSON):');
console.log(JSON.stringify(apiEvent, null, 2));
console.log('');
}
// 5. Recommandations
console.log('╔══════════════════════════════════════════════════════════════╗');
console.log('║ DIAGNOSTIC RÉSULTAT ║');
console.log('╚══════════════════════════════════════════════════════════════╝\n');
const sampleEvent = realEvents.rows[0] || testDates[0];
const isDateObject = typeof sampleEvent?.start_time === 'object';
if (isDateObject) {
console.log('❌ PROBLÈME TROUVÉ:\n');
console.log(' Le driver pg renvoie toujours des objets Date');
console.log(' Le types.setTypeParser(1184) n\'a PAS été appliqué\n');
console.log('💡 SOLUTION:\n');
console.log(' 1. Vérifier que config/database.js a bien:');
console.log(' const { Pool, types } = require(\'pg\');');
console.log(' types.setTypeParser(1184, ...) AVANT le Pool\n');
console.log(' 2. Rebuild: docker-compose build notes-app\n');
console.log(' 3. Restart: docker-compose restart notes-app\n');
} else {
console.log('✅ Le parser fonctionne!\n');
console.log(' Les dates sont renvoyées comme strings');
console.log(' Le problème doit être ailleurs (frontend?)\n');
}
// Cleanup
await client.query('DELETE FROM calendar_events WHERE google_event_id LIKE $1', ['diagnostic-%']);
client.release();
await pool.end();
} catch (error) {
console.error('\n❌ Erreur:', error.message);
console.error(error.stack);
process.exit(1);
}
}
fullDiagnosis();