1

Normalisation des dates/heures

This commit is contained in:
2025-12-21 23:14:36 +01:00
parent 3295596a8d
commit 12d0a801de
4 changed files with 186 additions and 18 deletions

View File

@@ -0,0 +1,125 @@
#!/usr/bin/env node
const fs = require("fs/promises");
const path = require("path");
const {
extractRawDate,
readFrontmatter,
writeFrontmatter,
} = require("./lib/weather/frontmatter");
const { parseFrontmatterDate, formatDateTime, getHugoTimeZone } = require("./lib/datetime");
const CONTENT_ROOT = path.join(process.cwd(), "content");
/**
* Liste récursivement tous les fichiers Markdown d'un dossier.
* @param {string} root Dossier racine à parcourir.
* @returns {Promise<string[]>} Chemins absolus des fichiers trouvés.
*/
async function listMarkdownFiles(root) {
const entries = await fs.readdir(root, { withFileTypes: true });
const results = [];
for (const entry of entries) {
const fullPath = path.join(root, entry.name);
if (entry.isDirectory()) {
const nested = await listMarkdownFiles(fullPath);
results.push(...nested);
continue;
}
if (entry.isFile() && entry.name.endsWith(".md")) {
results.push(fullPath);
}
}
return results;
}
/**
* Harmonise la date d'un fichier Markdown si nécessaire.
* @param {string} filePath Chemin absolu du fichier.
* @returns {Promise<"updated"|"unchanged"|"skipped"|"invalid">} Statut de traitement.
*/
async function normalizeFileDate(filePath) {
const frontmatter = await readFrontmatter(filePath);
if (!frontmatter) {
return "skipped";
}
const rawDate = extractRawDate(frontmatter.frontmatterText);
const dateValue = frontmatter.doc.get("date");
if (!rawDate && (dateValue === undefined || dateValue === null)) {
return "skipped";
}
const sourceValue = rawDate !== null ? rawDate : dateValue;
const parsed = parseFrontmatterDate(sourceValue);
if (!parsed) {
return "invalid";
}
let hasTime = false;
if (typeof rawDate === "string") {
const timeMatch = rawDate.match(/[T ](\d{2}):(\d{2})(?::(\d{2}))?/);
if (timeMatch) {
const hour = Number(timeMatch[1]);
const minute = Number(timeMatch[2]);
const second = Number(timeMatch[3] || "0");
hasTime = !(hour === 0 && minute === 0 && second === 0);
}
}
const normalized = hasTime
? parsed.set({ millisecond: 0 })
: parsed.set({ hour: 12, minute: 0, second: 0, millisecond: 0 });
const formatted = formatDateTime(normalized);
const current = typeof dateValue === "string" ? dateValue.trim() : rawDate;
const quotedFormatted = `"${formatted}"`;
const currentComparable = typeof current === "string" ? current.trim() : "";
if (currentComparable === formatted || currentComparable === quotedFormatted) {
return "unchanged";
}
frontmatter.doc.set("date", formatted);
await writeFrontmatter(filePath, frontmatter.doc, frontmatter.body);
const rewritten = await fs.readFile(filePath, "utf8");
const normalizedContent = rewritten.replace(/^date:\s*.+$/m, `date: ${quotedFormatted}`);
if (rewritten !== normalizedContent) {
await fs.writeFile(filePath, normalizedContent, "utf8");
}
return "updated";
}
/**
* Point d'entrée du script.
*/
async function main() {
const timezone = getHugoTimeZone();
console.log(`Fuseau horaire Hugo : ${timezone}`);
const files = await listMarkdownFiles(CONTENT_ROOT);
let updated = 0;
let unchanged = 0;
let skipped = 0;
let invalid = 0;
for (const file of files) {
const status = await normalizeFileDate(file);
if (status === "updated") updated += 1;
else if (status === "unchanged") unchanged += 1;
else if (status === "invalid") {
invalid += 1;
const relative = path.relative(process.cwd(), file);
console.warn(`Date invalide : ${relative}`);
} else {
skipped += 1;
}
}
console.log(
`Terminé. ${updated} mis à jour, ${unchanged} inchangés, ${skipped} ignorés, ${invalid} invalides.`
);
}
main();