/** * Fix Script: Pad Single-Digit Hours in CreatedTime * This script fixes times like "2:31:47 PM" to "02:31:47 PM" * * Usage: node scripts/fixSingleDigitHours.js [--dry-run] */ import { rawQuery } from '../src/db.js'; import dotenv from 'dotenv'; dotenv.config(); async function fixSingleDigitHours(dryRun = false) { console.log('šŸ” Checking for single-digit hour times...\n'); try { // First, find all entries with single-digit hours const problematic = await rawQuery(` SELECT ctid, "CreatedDate", "CreatedTime", "Symbol" FROM "OptionsFlow_monthly" WHERE "CreatedTime" IS NOT NULL AND ( -- Single-digit hour with seconds: "2:31:47 PM" "CreatedTime"::text ~ '^[1-9]:[0-9]{2}:[0-9]{2}\\s*(AM|PM)$' -- Single-digit hour with minutes only: "2:30 PM" OR "CreatedTime"::text ~ '^[1-9]:[0-9]{2}\\s*(AM|PM)$' ) ORDER BY "CreatedDate" DESC, "CreatedTime" DESC LIMIT 100; `); console.log(`šŸ“Š Found ${problematic.length} entries with single-digit hours`); if (problematic.length === 0) { console.log('āœ… No entries with single-digit hours found!'); return; } // Show sample console.log('\nšŸ“‹ Sample of problematic entries:'); problematic.slice(0, 10).forEach(row => { console.log(` - "${row.CreatedTime}" on ${row.CreatedDate} (${row.Symbol})`); }); if (dryRun) { console.log('\nšŸ” DRY RUN MODE - No changes will be made'); console.log(` Would fix ${problematic.length} entries`); return; } // Count total affected const totalCount = await rawQuery(` SELECT COUNT(*) as total FROM "OptionsFlow_monthly" WHERE "CreatedTime" IS NOT NULL AND ( "CreatedTime"::text ~ '^[1-9]:[0-9]{2}:[0-9]{2}\\s*(AM|PM)$' OR "CreatedTime"::text ~ '^[1-9]:[0-9]{2}\\s*(AM|PM)$' ); `); const total = totalCount[0]?.total || 0; console.log(`\nšŸ”§ Fixing ${total} entries...`); // Fix entries with seconds: "2:31:47 PM" -> "02:31:47 PM" const fixWithSeconds = await rawQuery(` UPDATE "OptionsFlow_monthly" SET "CreatedTime" = REGEXP_REPLACE( btrim("CreatedTime"::text), '^([1-9])(:[0-9]{2}:[0-9]{2}\\s*(AM|PM))', '0\\1\\2', 'i' ) WHERE "CreatedTime" IS NOT NULL AND "CreatedTime"::text ~ '^[1-9]:[0-9]{2}:[0-9]{2}\\s*(AM|PM)$'; `); console.log(` āœ… Fixed entries with seconds format`); // Fix entries with minutes only: "2:30 PM" -> "02:30 PM" const fixWithMinutes = await rawQuery(` UPDATE "OptionsFlow_monthly" SET "CreatedTime" = REGEXP_REPLACE( btrim("CreatedTime"::text), '^([1-9])(:[0-9]{2}\\s*(AM|PM))', '0\\1\\2', 'i' ) WHERE "CreatedTime" IS NOT NULL AND "CreatedTime"::text ~ '^[1-9]:[0-9]{2}\\s*(AM|PM)$'; `); console.log(` āœ… Fixed entries with minutes format`); // Verify the fix const remaining = await rawQuery(` SELECT COUNT(*) as total FROM "OptionsFlow_monthly" WHERE "CreatedTime" IS NOT NULL AND ( "CreatedTime"::text ~ '^[1-9]:[0-9]{2}:[0-9]{2}\\s*(AM|PM)$' OR "CreatedTime"::text ~ '^[1-9]:[0-9]{2}\\s*(AM|PM)$' ); `); const remainingCount = remaining[0]?.total || 0; if (remainingCount === 0) { console.log(`\nāœ… Successfully fixed all ${total} entries!`); } else { console.log(`\nāš ļø Warning: ${remainingCount} entries still have single-digit hours`); console.log(' This might be due to whitespace or other formatting issues'); } // Show sample of fixed entries const fixedSample = await rawQuery(` SELECT "CreatedDate", "CreatedTime", COUNT(*) as count FROM "OptionsFlow_monthly" WHERE "CreatedTime" IS NOT NULL AND "CreatedTime"::text ~ '^[0-9]{2}:[0-9]{2}:[0-9]{2}\\s*(AM|PM)$' GROUP BY "CreatedDate", "CreatedTime" ORDER BY "CreatedDate" DESC LIMIT 5; `); console.log('\nšŸ“‹ Sample of fixed entries (now properly formatted):'); fixedSample.forEach(row => { console.log(` - "${row.CreatedTime}" on ${row.CreatedDate}: ${row.count} occurrences`); }); } catch (error) { console.error('āŒ Error fixing single-digit hours:', error.message); console.error(error); throw error; } } // Parse command line arguments const args = process.argv.slice(2); const dryRun = args.includes('--dry-run') || args.includes('-d'); // Run the fix fixSingleDigitHours(dryRun) .then(() => { console.log('\nāœ… Fix complete'); process.exit(0); }) .catch((error) => { console.error('\nāŒ Fix failed:', error); process.exit(1); });