mirror of
https://github.com/Sea-Haven-Industries/payments-dashboard.git
synced 2026-10-01 11:33:12 +00:00
54 lines
2.1 KiB
JavaScript
54 lines
2.1 KiB
JavaScript
|
|
// Shared date parsing for Stampli M/D/YY and MM/DD/YYYY formats.
|
||
|
|
// Stampli exports switched from MM/DD/YYYY to M/D/YY in 2026-06/07; both
|
||
|
|
// formats must parse, and anything else must be rejected (null), never
|
||
|
|
// passed through as a malformed string (payments-dashboard#65).
|
||
|
|
|
||
|
|
// Parse "M/D/YY" or "MM/DD/YYYY" into { year, month, day }, or null.
|
||
|
|
// Two-digit years are 2000-based. Validates the date is real (no 2/30).
|
||
|
|
export function parseMDY(value) {
|
||
|
|
const match = /^(\d{1,2})\/(\d{1,2})\/(\d{2}|\d{4})$/.exec(String(value ?? "").trim());
|
||
|
|
if (!match) return null;
|
||
|
|
const month = parseInt(match[1], 10);
|
||
|
|
const day = parseInt(match[2], 10);
|
||
|
|
let year = parseInt(match[3], 10);
|
||
|
|
if (match[3].length === 2) year += 2000;
|
||
|
|
const dt = new Date(year, month - 1, day);
|
||
|
|
if (dt.getFullYear() !== year || dt.getMonth() !== month - 1 || dt.getDate() !== day) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
return { year, month, day };
|
||
|
|
}
|
||
|
|
|
||
|
|
// "YYYY-MM-DD" or null.
|
||
|
|
export function toISODate(value) {
|
||
|
|
const p = parseMDY(value);
|
||
|
|
if (!p) return null;
|
||
|
|
return `${p.year}-${String(p.month).padStart(2, "0")}-${String(p.day).padStart(2, "0")}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Canonical "MM/DD/YYYY" or null. Stored form for send_payment_on.
|
||
|
|
export function toCanonicalMDY(value) {
|
||
|
|
const p = parseMDY(value);
|
||
|
|
if (!p) return null;
|
||
|
|
return `${String(p.month).padStart(2, "0")}/${String(p.day).padStart(2, "0")}/${p.year}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Local-midnight Date or null. Replaces slackAppHome's parseMDYLocal, which
|
||
|
|
// built year-1926 dates from 2-digit years via new Date(26, ...).
|
||
|
|
export function parseMDYLocal(value) {
|
||
|
|
const p = parseMDY(value);
|
||
|
|
if (!p) return null;
|
||
|
|
return new Date(p.year, p.month - 1, p.day);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Plausibility window for ingested send dates. Anything outside is treated
|
||
|
|
// as parser garbage and the row is rejected loudly rather than stored.
|
||
|
|
// Relative to the current year so it never expires (7 years back covers
|
||
|
|
// historical re-ingests; 2 years forward covers future-scheduled checks).
|
||
|
|
export function isPlausibleSendYear(value) {
|
||
|
|
const p = parseMDY(value);
|
||
|
|
if (p === null) return false;
|
||
|
|
const now = new Date().getFullYear();
|
||
|
|
return p.year >= now - 7 && p.year <= now + 2;
|
||
|
|
}
|