rsshub
Version:
Make RSS Great Again!
80 lines (79 loc) • 2.55 kB
JavaScript
//#region lib/routes/lg/utils.ts
/** Minimal RFC 4180 CSV parser: quoted fields, `""` escapes, newlines inside quotes, CRLF. */
const parseCsv = (text) => {
const rows = [];
let row = [];
let field = "";
let quoted = false;
for (let i = 0; i < text.length; i++) {
const c = text[i];
if (quoted) {
if (c !== "\"") field += c;
else if (text[i + 1] === "\"") {
field += "\"";
i++;
} else quoted = false;
continue;
}
switch (c) {
case "\"":
quoted = true;
break;
case ",":
row.push(field);
field = "";
break;
case "\r":
case "\n":
if (c === "\r" && text[i + 1] === "\n") i++;
row.push(field);
rows.push(row);
row = [];
field = "";
break;
default: field += c;
}
}
if (field !== "" || row.length > 0) {
row.push(field);
rows.push(row);
}
return rows;
};
/** CSV text → records keyed by the (trimmed) header row; a missing cell is `null`. */
const csvRecords = (text) => {
const [header, ...rows] = parseCsv(text);
if (!header) return [];
const keys = header.map((h) => h.trim());
return rows.filter((r) => r.length > 1).map((r) => Object.fromEntries(keys.map((k, i) => [k, r[i] ?? null])));
};
const ERA_BASE = {
令和: 2018,
平成: 1988,
昭和: 1925
};
/** `2024-04-08` / `2026/8/7` / `令和8年8月7日` → `YYYY-MM-DD`; anything else → null. */
const isoDate = (raw) => {
if (raw === null) return null;
const s = raw.normalize("NFKC").trim();
const m = /^(\d{4})[-/](\d{1,2})[-/](\d{1,2})/.exec(s);
if (m) return `${m[1]}-${m[2].padStart(2, "0")}-${m[3].padStart(2, "0")}`;
const w = /^(令和|平成|昭和)(元|\d{1,2})年(\d{1,2})月(\d{1,2})日/.exec(s);
if (!w) return null;
return `${ERA_BASE[w[1]] + (w[2] === "元" ? 1 : Number(w[2]))}-${w[3].padStart(2, "0")}-${w[4].padStart(2, "0")}`;
};
/** Ward sites serve CSV as UTF-8 (with BOM) one month and Shift_JIS the next, so sniff: strict UTF-8 first, else Shift_JIS. */
const decodeText = (buf) => {
try {
return new TextDecoder("utf-8", { fatal: true }).decode(buf);
} catch {
return new TextDecoder("shift_jis").decode(buf);
}
};
/** Sort key for `令和8年7月分`-style labels (full-width digits allowed): `2026-07`; null when absent. */
const warekiMonth = (label) => {
const m = /(令和|平成)(元|\d{1,2})年(\d{1,2})月/.exec(label.normalize("NFKC"));
return m ? `${ERA_BASE[m[1]] + (m[2] === "元" ? 1 : Number(m[2]))}-${m[3].padStart(2, "0")}` : null;
};
//#endregion
export { warekiMonth as i, decodeText as n, isoDate as r, csvRecords as t };