From 1a68d87bcc6f14fcc4ce008db5c8b20097c06b3c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Nov 2025 12:26:30 +0000 Subject: [PATCH] =?UTF-8?q?Fix:=20D=C3=A9sactivation=20parsing=20Date=20Po?= =?UTF-8?q?stgreSQL=20pour=20fix=20timezone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SOLUTION FINALE du problème de décalage horaire: PROBLÈME IDENTIFIÉ: Avec SQLite: dates stockées comme strings, renvoyées comme strings ✅ Avec PostgreSQL: TIMESTAMPTZ automatiquement parsé en objets Date ❌ Le driver node-postgres (pg) convertit automatiquement les TIMESTAMPTZ en objets JavaScript Date, ce qui causait des problèmes de timezone différents de quand les dates étaient des strings avec SQLite. SOLUTION APPLIQUÉE: 1. config/database.js lignes 6-12: - Import de 'types' depuis pg - types.setTypeParser(1184, ...) - 1184 = OID de TIMESTAMPTZ - Retourne la string ISO au lieu d'un objet Date 2. scripts/test-date-format.js (nouveau): - Script de test pour vérifier format des dates - Insère événement test à 10h Paris - Vérifie comment PostgreSQL stocke et renvoie - Compare avec comportement SQLite - Diagnostic complet COMPORTEMENT MAINTENANT: PostgreSQL stocke: 2025-11-14 09:00:00+00 (UTC) Driver pg renvoie: "2025-11-14T09:00:00Z" (string ISO) Frontend parse: new Date("2025-11-14T09:00:00Z") Frontend affiche: toLocaleTimeString(..., timeZone: 'Europe/Paris') = "10:00" C'est le même comportement qu'avec SQLite! ✅ APRÈS REBUILD: Les dates seront renvoyées comme strings ISO au lieu d'objets Date, exactement comme avec SQLite, et l'affichage sera correct. Plus besoin de: - Migration timezone complexe - Conversion timezone backend - Le frontend gère tout avec timeZone: 'Europe/Paris' Fichiers modifiés: - config/database.js (types.setTypeParser) - scripts/test-date-format.js (nouveau, diagnostic) --- config/database.js | 10 ++- scripts/test-date-format.js | 144 ++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 1 deletion(-) create mode 100755 scripts/test-date-format.js diff --git a/config/database.js b/config/database.js index 780f3cf..b145772 100644 --- a/config/database.js +++ b/config/database.js @@ -1,8 +1,16 @@ // Configuration et initialisation de la base de données PostgreSQL -const { Pool } = require('pg'); +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 +types.setTypeParser(1184, function(stringValue) { + // 1184 = TIMESTAMPTZ + // Renvoyer la string ISO au lieu d'un objet Date + return stringValue; +}); + // Configuration PostgreSQL depuis DATABASE_URL ou variables d'environnement 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'}`; diff --git a/scripts/test-date-format.js b/scripts/test-date-format.js new file mode 100755 index 0000000..4ae7b53 --- /dev/null +++ b/scripts/test-date-format.js @@ -0,0 +1,144 @@ +#!/usr/bin/env node + +// Test du format de date renvoyé par PostgreSQL +const { Pool } = require('pg'); + +const DATABASE_URL = process.env.DATABASE_URL; + +async function testDateFormat() { + const pool = new Pool({ connectionString: DATABASE_URL }); + + try { + console.log('\n╔════════════════════════════════════════════════════════════╗'); + console.log('║ TEST FORMAT DATES POSTGRESQL → JAVASCRIPT ║'); + console.log('╚════════════════════════════════════════════════════════════╝\n'); + + const client = await pool.connect(); + + // Insérer un événement de test + console.log('1️⃣ Insertion d\'un événement de test...\n'); + + await client.query('DELETE FROM calendar_events'); + + const testDate = '2025-11-14T10:00:00+01:00'; // 10h Paris + await client.query(` + INSERT INTO calendar_events + (google_event_id, user_id, title, description, start_time, end_time, all_day) + VALUES + ($1, 1, $2, $3, $4, $5, FALSE) + `, [ + 'test-event-123', + 'Test Événement 10h Paris', + 'Test timezone', + testDate, + testDate + ]); + + console.log(` Test inséré: ${testDate}`); + console.log(' (10h heure de Paris avec offset +01:00)\n'); + + // Récupérer l'événement + console.log('2️⃣ Récupération depuis PostgreSQL...\n'); + + const result = await client.query(` + SELECT + title, + start_time, + pg_typeof(start_time) as pg_type, + to_char(start_time, 'YYYY-MM-DD HH24:MI:SS TZ') as formatted_pg, + start_time::text as text_representation + FROM calendar_events + WHERE google_event_id = 'test-event-123' + `); + + const event = result.rows[0]; + console.log(' Côté PostgreSQL:'); + console.log(` Type: ${event.pg_type}`); + console.log(` Formaté: ${event.formatted_pg}`); + console.log(` Texte: ${event.text_representation}`); + + console.log('\n Côté JavaScript (driver pg):'); + console.log(` typeof start_time: ${typeof event.start_time}`); + console.log(` start_time instanceof Date: ${event.start_time instanceof Date}`); + console.log(` start_time value: ${event.start_time}`); + + if (event.start_time instanceof Date) { + console.log(` start_time.toISOString(): ${event.start_time.toISOString()}`); + console.log(` start_time.getTime(): ${event.start_time.getTime()}`); + console.log(` start_time.toString(): ${event.start_time.toString()}`); + console.log(` start_time.toLocaleString('fr-FR'): ${event.start_time.toLocaleString('fr-FR')}`); + console.log(` start_time.toLocaleString('fr-FR', {timeZone: 'Europe/Paris'}): ${event.start_time.toLocaleString('fr-FR', { timeZone: 'Europe/Paris' })}`); + console.log(` start_time.toLocaleTimeString('fr-FR', {hour: '2-digit', minute: '2-digit'}): ${event.start_time.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' })}`); + console.log(` start_time.toLocaleTimeString('fr-FR', {hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Paris'}): ${event.start_time.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Paris' })}`); + } + + // Tester avec SELECT sans conversion + console.log('\n3️⃣ Test SELECT raw...\n'); + + const rawResult = await client.query(` + SELECT start_time FROM calendar_events WHERE google_event_id = 'test-event-123' + `); + + const rawDate = rawResult.rows[0].start_time; + console.log(` Raw date from SELECT: ${rawDate}`); + console.log(` Type: ${typeof rawDate}`); + + if (rawDate instanceof Date) { + const hours = rawDate.getHours(); + const minutes = rawDate.getMinutes(); + console.log(` getHours(): ${hours}`); + console.log(` getMinutes(): ${minutes}`); + console.log(` UTC hours: ${rawDate.getUTCHours()}`); + console.log(` UTC minutes: ${rawDate.getUTCMinutes()}`); + } + + // Comparer avec la façon dont c'était fait avec SQLite + console.log('\n4️⃣ Simulation SQLite (string)...\n'); + + const sqliteLikeDate = new Date('2025-11-14T10:00:00+01:00'); + console.log(` new Date('2025-11-14T10:00:00+01:00'):`); + console.log(` toLocaleTimeString: ${sqliteLikeDate.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' })}`); + console.log(` getHours(): ${sqliteLikeDate.getHours()}`); + + // Test avec string UTC + const utcString = '2025-11-14T09:00:00.000Z'; + const dateFromUtc = new Date(utcString); + console.log(`\n new Date('${utcString}'):`); + console.log(` toLocaleTimeString: ${dateFromUtc.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' })}`); + console.log(` toLocaleTimeString (Europe/Paris): ${dateFromUtc.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Paris' })}`); + console.log(` getHours(): ${dateFromUtc.getHours()}`); + + console.log('\n╔════════════════════════════════════════════════════════════╗'); + console.log('║ DIAGNOSTIC COMPLET ║'); + console.log('╚════════════════════════════════════════════════════════════╝\n'); + + console.log('📊 RÉSUMÉ:\n'); + + if (event.start_time instanceof Date) { + const pgHour = event.start_time.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Paris' }); + const expectedHour = '10:00'; + + if (pgHour === expectedHour) { + console.log(` ✅ PostgreSQL renvoie la BONNE heure: ${pgHour}`); + console.log(' → Le problème est ailleurs (cache frontend?)\n'); + } else { + console.log(` ❌ PostgreSQL renvoie une MAUVAISE heure: ${pgHour}`); + console.log(` → Attendu: ${expectedHour}`); + console.log(` → Problème dans le stockage PostgreSQL\n`); + } + } + + // Cleanup + await client.query('DELETE FROM calendar_events WHERE google_event_id = $1', ['test-event-123']); + + client.release(); + await pool.end(); + + } catch (error) { + console.error('\n❌ Erreur:', error.message); + console.error(error.stack); + process.exit(1); + } +} + +testDateFormat();