191 lines
5.7 KiB
JavaScript
191 lines
5.7 KiB
JavaScript
const fs = require("fs");
|
|
const path = require("path");
|
|
const YAML = require("yaml");
|
|
const { DateTime } = require("luxon");
|
|
|
|
const HUGO_CONFIG_PATH = path.join(process.cwd(), "config", "_default", "config.yaml");
|
|
|
|
let cachedTimeZone = null;
|
|
|
|
/**
|
|
* Récupère le fuseau horaire configuré pour Hugo.
|
|
* @returns {string} Identifiant IANA du fuseau horaire de Hugo.
|
|
*/
|
|
function getHugoTimeZone() {
|
|
if (cachedTimeZone) {
|
|
return cachedTimeZone;
|
|
}
|
|
|
|
const rawConfig = fs.readFileSync(HUGO_CONFIG_PATH, "utf8");
|
|
const parsedConfig = YAML.parse(rawConfig);
|
|
|
|
if (!parsedConfig || !parsedConfig.timeZone) {
|
|
throw new Error("Aucun fuseau horaire Hugo n'a été trouvé dans config/_default/config.yaml.");
|
|
}
|
|
|
|
cachedTimeZone = String(parsedConfig.timeZone).trim();
|
|
|
|
if (!cachedTimeZone) {
|
|
throw new Error("Le fuseau horaire Hugo est vide ou invalide.");
|
|
}
|
|
|
|
return cachedTimeZone;
|
|
}
|
|
|
|
/**
|
|
* Parse une chaîne de date selon les formats Hugo attendus.
|
|
* @param {string} value Chaîne de date.
|
|
* @param {string} zone Fuseau horaire IANA.
|
|
* @param {number} defaultHour Heure par défaut si absente.
|
|
* @param {number} defaultMinute Minute par défaut si absente.
|
|
* @returns {import("luxon").DateTime|null} DateTime ou null si invalide.
|
|
*/
|
|
function parseHugoDateString(value, zone, defaultHour = 12, defaultMinute = 0) {
|
|
let trimmed = value.trim();
|
|
if (!trimmed) {
|
|
return null;
|
|
}
|
|
if (
|
|
(trimmed.startsWith("'") && trimmed.endsWith("'")) ||
|
|
(trimmed.startsWith("\"") && trimmed.endsWith("\""))
|
|
) {
|
|
trimmed = trimmed.slice(1, -1).trim();
|
|
}
|
|
|
|
const iso = DateTime.fromISO(trimmed, { setZone: true });
|
|
if (iso.isValid) {
|
|
return iso.setZone(zone);
|
|
}
|
|
|
|
const formats = [
|
|
"yyyy-LL-dd HH:mm:ss",
|
|
"yyyy-LL-dd'T'HH:mm:ss",
|
|
"yyyy-LL-dd HH:mm",
|
|
"yyyy-LL-dd'T'HH:mm",
|
|
"yyyy-LL-dd",
|
|
];
|
|
|
|
for (const format of formats) {
|
|
const parsed = DateTime.fromFormat(trimmed, format, { zone });
|
|
if (parsed.isValid) {
|
|
if (format === "yyyy-LL-dd") {
|
|
return parsed.set({ hour: defaultHour, minute: defaultMinute, second: 0, millisecond: 0 });
|
|
}
|
|
return parsed;
|
|
}
|
|
}
|
|
|
|
const rfc2822 = DateTime.fromRFC2822(trimmed, { setZone: true });
|
|
if (rfc2822.isValid) {
|
|
return rfc2822.setZone(zone);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Convertit une valeur vers un DateTime positionné sur le fuseau horaire Hugo.
|
|
* @param {Date|import("luxon").DateTime|string|number|null} value Valeur à convertir (null : maintenant).
|
|
* @returns {import("luxon").DateTime} Instance DateTime alignée sur le fuseau de Hugo.
|
|
*/
|
|
function toHugoDateTime(value = null) {
|
|
const zone = getHugoTimeZone();
|
|
|
|
if (value === null || value === undefined) {
|
|
const now = DateTime.now().setZone(zone);
|
|
if (!now.isValid) {
|
|
throw new Error(now.invalidReason || "Date actuelle invalide pour le fuseau Hugo.");
|
|
}
|
|
return now;
|
|
}
|
|
|
|
if (DateTime.isDateTime(value)) {
|
|
const zoned = value.setZone(zone);
|
|
if (!zoned.isValid) {
|
|
throw new Error(zoned.invalidReason || "DateTime invalide après application du fuseau Hugo.");
|
|
}
|
|
return zoned;
|
|
}
|
|
|
|
if (value instanceof Date) {
|
|
const zoned = DateTime.fromJSDate(value, { zone });
|
|
if (!zoned.isValid) {
|
|
throw new Error(zoned.invalidReason || "Date JS invalide pour le fuseau Hugo.");
|
|
}
|
|
return zoned;
|
|
}
|
|
|
|
if (typeof value === "string") {
|
|
const parsed = parseHugoDateString(value, zone);
|
|
if (!parsed) {
|
|
throw new Error(`Chaîne de date invalide : ${value}`);
|
|
}
|
|
return parsed;
|
|
}
|
|
|
|
if (typeof value === "number") {
|
|
const parsed = DateTime.fromMillis(value, { zone }).setZone(zone);
|
|
if (!parsed.isValid) {
|
|
throw new Error(parsed.invalidReason || "Horodatage numérique invalide pour le fuseau Hugo.");
|
|
}
|
|
return parsed;
|
|
}
|
|
|
|
throw new Error("Type de date non pris en charge pour le fuseau horaire Hugo.");
|
|
}
|
|
|
|
/**
|
|
* Formate une date selon le format Hugo simple (sans offset).
|
|
* @param {Date|import("luxon").DateTime|string|number|null} value Valeur à formater.
|
|
* @returns {string} Date formatée.
|
|
*/
|
|
function formatDateTime(value = null) {
|
|
const zoned = toHugoDateTime(value);
|
|
const normalized = zoned.set({ millisecond: 0 });
|
|
const formatted = normalized.toFormat("yyyy-LL-dd HH:mm:ss");
|
|
|
|
if (!formatted) {
|
|
throw new Error("Impossible de formater la date avec le fuseau Hugo.");
|
|
}
|
|
|
|
return formatted;
|
|
}
|
|
|
|
/**
|
|
* Convertit une valeur de frontmatter en DateTime si elle est valide.
|
|
* @param {import("luxon").DateTime|Date|string|number|null|undefined} value Valeur lue depuis le frontmatter.
|
|
* @returns {import("luxon").DateTime|null} DateTime utilisable ou null si invalide.
|
|
*/
|
|
function parseFrontmatterDate(value) {
|
|
const zone = getHugoTimeZone();
|
|
|
|
if (DateTime.isDateTime(value)) {
|
|
const zoned = value.setZone(zone);
|
|
return zoned.isValid ? zoned : null;
|
|
}
|
|
|
|
if (value instanceof Date) {
|
|
const zoned = DateTime.fromJSDate(value, { zone });
|
|
return zoned.isValid ? zoned : null;
|
|
}
|
|
|
|
if (typeof value === "string") {
|
|
const parsed = parseHugoDateString(value, zone);
|
|
return parsed;
|
|
}
|
|
|
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
const zoned = DateTime.fromMillis(value, { zone }).setZone(zone);
|
|
return zoned.isValid ? zoned : null;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
module.exports = {
|
|
formatDateTime,
|
|
getHugoTimeZone,
|
|
toHugoDateTime,
|
|
parseFrontmatterDate,
|
|
};
|