From 29583c55ed045af3675dd9de2b10de7c64a4ab4c Mon Sep 17 00:00:00 2001 From: Rudy Celekli Date: Wed, 14 Oct 2026 20:12:29 -0400 Subject: [PATCH] fix(firms): reject impossible acquisition days in CSV parsing Rebased onto current main with review follow-ups folded in. Co-Authored-By: Claude Sonnet 5.5 --- CHANGELOG.md | 2 ++ docs/CURRENT-STATE.md | 2 ++ src/data/firms-calendar-validity.test.mjs | 23 +++++++++++++++++++++++ src/data/firmsCsv.js | 8 +++++++- 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/data/firms-calendar-validity.test.mjs diff --git a/CHANGELOG.md b/CHANGELOG.md index a770fbd35..ca4974a0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,8 @@ - Director timeline seeks cross the dateline along the short longitude arc. +- Reject impossible FIRMS acquisition calendar days in the CSV parser before trailing-24-hour filtering. + - Treat explicitly current, complete empty vessel coverage as a successful update, clearing obsolete contacts while retaining stale or incomplete snapshots. diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 1f794a5b5..6a4f3061b 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -1429,6 +1429,8 @@ accessible name remains "Don't show this again". Control behavior is unchanged. ## FIRMS source status +The FIRMS CSV parser rejects impossible acquisition calendar dates before the proxy filters records to the trailing 24 hours, while retaining valid UTC acquisition times. + The FIRMS proxy records source success after appending its rows. If aggregation throws, that source reports failure without a contradictory success entry. The existing per-record append continues to support large feeds; sequential diff --git a/src/data/firms-calendar-validity.test.mjs b/src/data/firms-calendar-validity.test.mjs new file mode 100644 index 000000000..1ccaf7435 --- /dev/null +++ b/src/data/firms-calendar-validity.test.mjs @@ -0,0 +1,23 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { acquisitionMsUtc } from './firmsCsv.js'; +test('FIRMS acquisition dates cannot roll into a different calendar day', () => { + for (const [date, time] of [ + ['2026-02-30', '1200'], + ['2026-04-31', '1200'], + ['2026-13-01', '1200'], + ['2026-07-16junk', '1200'], + ['2026-07-16', '2400'], + ['2026-07-16', '1260'], + ['2026-07-16', null], + ['2026-07-16', ''], + ]) { + assert.ok(Number.isNaN(acquisitionMsUtc(date, time)), `${date}:${time}`); + } + for (const [date, time, expected] of [ + ['2024-02-29', '45', Date.UTC(2024, 1, 29, 0, 45)], + ['2026-07-16', '0', Date.UTC(2026, 6, 16)], + ]) { + assert.equal(acquisitionMsUtc(date, time), expected); + } +}); diff --git a/src/data/firmsCsv.js b/src/data/firmsCsv.js index 7f9019e18..53d132d99 100644 --- a/src/data/firmsCsv.js +++ b/src/data/firmsCsv.js @@ -153,7 +153,13 @@ export function acquisitionMsUtc(acqDate, acqTime) { minutes > 59 ) return NaN; - return Date.UTC(year, month - 1, day, hours, minutes); + const ms = Date.UTC(year, month - 1, day, hours, minutes); + const parsed = new Date(ms); + return parsed.getUTCFullYear() === year && + parsed.getUTCMonth() === month - 1 && + parsed.getUTCDate() === day + ? ms + : NaN; } /**