@tricoteuses/senat
Version:
Handle French Sénat's open data
127 lines (126 loc) • 4.16 kB
JavaScript
import { DateTime } from "luxon";
export function yyyymmddFromPath(xmlFilePath) {
return xmlFilePath.replace(/^.*?(\d{8}).*$/i, "$1");
}
export function parseYYYYMMDD(yyyymmdd) {
if (!/^\d{8}$/.test(yyyymmdd))
return null;
const y = Number(yyyymmdd.slice(0, 4));
const m = Number(yyyymmdd.slice(4, 6)) - 1;
const d = Number(yyyymmdd.slice(6, 8));
const dt = new Date(y, m, d);
return Number.isFinite(dt.getTime()) ? dt : null;
}
export function frDateToISO(s) {
if (!s)
return;
const months = {
janvier: 1,
février: 2,
fevrier: 2,
mars: 3,
avril: 4,
mai: 5,
juin: 6,
juillet: 7,
août: 8,
aout: 8,
septembre: 9,
octobre: 10,
novembre: 11,
décembre: 12,
decembre: 12,
};
const cleaned = s
.trim()
.replace(/\u00A0/g, " ")
.replace(/ +/g, " ");
const m = cleaned.match(/^(\d{1,2})(?:er)?\s+([a-zéèêîïôûùç]+)\s+(\d{4})$/i);
if (!m)
return;
const d = String(parseInt(m[1], 10)).padStart(2, "0");
const mon = months[m[2].toLowerCase()];
if (!mon)
return;
const y = m[3];
return `${y}-${String(mon).padStart(2, "0")}-${d}`;
}
export function hourShortToStartTime(hourShort) {
if (!hourShort || hourShort === "NA")
return null;
if (!/^\d{4}$/.test(hourShort))
return null;
const hh = hourShort.slice(0, 2);
const mm = hourShort.slice(2, 4);
return `${hh}:${mm}`;
}
export function epochToParisDateTime(epochSec) {
if (!Number.isFinite(epochSec))
return null;
const dUtc = new Date(epochSec * 1000);
// Offset heuristic (same logique que parisOffsetForDate)
const m = dUtc.getUTCMonth() + 1; // 1..12
const offsetHours = m >= 4 && m <= 10 ? 2 : 1;
const offsetStr = offsetHours === 2 ? "+02:00" : "+01:00";
// Applique l'offset pour obtenir la date/heure locales Paris
const localMs = dUtc.getTime() + offsetHours * 3600 * 1000;
const dl = new Date(localMs);
const yyyy = String(dl.getUTCFullYear());
const mm = String(dl.getUTCMonth() + 1).padStart(2, "0");
const dd = String(dl.getUTCDate()).padStart(2, "0");
const hh = String(dl.getUTCHours()).padStart(2, "0");
const mi = String(dl.getUTCMinutes()).padStart(2, "0");
const ss = String(dl.getUTCSeconds()).padStart(2, "0");
const ms = String(dl.getUTCMilliseconds()).padStart(3, "0");
return {
date: `${yyyy}-${mm}-${dd}`,
startTime: `${hh}:${mi}:${ss}.${ms}${offsetStr}`,
};
}
export function toTargetEpoch(time, date) {
if (!time)
return null;
let dtLocal;
if (time.includes("T")) {
dtLocal = DateTime.fromISO(time, { zone: "Europe/Paris" });
}
else if (date) {
dtLocal = DateTime.fromISO(`${date}T${time}`, { zone: "Europe/Paris" });
}
else {
return null;
}
if (!dtLocal.isValid)
return null;
return Math.floor(dtLocal.toUTC().toSeconds());
}
export function toFRDate(dateYYYYMMDD) {
const [y, m, d] = dateYYYYMMDD.split("-");
return `${d}/${m}/${y}`; // DD/MM/YYYY
}
export function isAmbiguousTimeOriginal(timeOriginal) {
if (!timeOriginal)
return false;
const s = timeOriginal.toLowerCase();
// Catches "14h", "14 h", "14h30", "14 h 30", "14 heures", "14 heure"
const timeRe = /\b([01]?\d|2[0-3])\s*(?:h|heures?|heure)\s*(?:([0-5]\d))?\b/g;
const times = new Set();
let m;
while ((m = timeRe.exec(s))) {
const hh = String(m[1]).padStart(2, "0");
const mm = m[2] ? String(m[2]).padStart(2, "0") : "00";
times.add(`${hh}:${mm}`);
}
// "midi" / "minuit"
if (/\bmidi\b/.test(s))
times.add("12:00");
if (/\bminuit\b/.test(s))
times.add("00:00");
if (times.size >= 2)
return true;
const hasDayPeriod = /\b(matin|après-?midi|soir|nuit|journée|toute la journée)\b/.test(s);
const hasLinking = /,|\bet\b|\bou\b|\bpuis\b/.test(s);
if (times.size === 1 && hasDayPeriod && hasLinking)
return true;
return false;
}