ScoDoc/app/static/js/date_utils.js

453 lines
14 KiB
JavaScript
Raw Permalink Normal View History

2023-11-15 14:11:47 +01:00
/**
* Transforme une date du format français (DD/MM/YYYY) au format iso (YYYY-MM-DD)
* Exemple d'utilisation :
* new Date(Date.fromFRA("30/06/2024")) -> new Date("2024-06-30")
* @param {string} dateFra
* @returns {string} dateIso
*/
Date.fromFRA = function (dateFra) {
if (dateFra == "") return "";
// Expression régulière pour valider le format de date ISO (YYYY-MM-DD)
const regexDateFra = /^(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[0-2])\/\d{4}$/;
// Vérification du format de la date ISO
if (!regexDateFra.test(dateFra)) {
throw new Error(
`La date (format français) passée en paramètre [${dateFra}] n'est pas valide.`
);
}
// Conversion du format français (DD/MM/YYYY) au format ISO (YYYY-MM-DD)
return `${dateFra.substring(6, 10)}-${dateFra.substring(
3,
5
)}-${dateFra.substring(0, 2)}`;
};
/**
* Transforme une date du format iso (YYYY-MM-DD) au format français (DD/MM/YYYY)
* Exemple d'utilisation :
* Date.toFRA("2024-06-30") -> "30/06/2024"
* @param {string} dateIso
* @returns {string} dateFra
*/
Date.toFRA = function (dateIso) {
if (dateIso == "") return "";
// Expression régulière pour valider le format de date ISO (YYYY-MM-DD)
const regexDateIso = /^\d{4}-(0\d|1[0-2])-([0-2]\d|3[01])$/;
// Vérification du format de la date ISO
if (!regexDateIso.test(dateIso)) {
throw new Error(
`La date ISO passée en paramètre [${dateIso}] n'est pas valide.`
);
}
// Conversion du format ISO (YYYY-MM-DD) en format français (DD/MM/YYYY)
return `${dateIso.substring(8, 10)}/${dateIso.substring(
5,
7
)}/${dateIso.substring(0, 4)}`;
};
/**
* Vérifie si le début de l'une des périodes est avant la fin de l'autre
* et si la fin de cette période est après le début de l'autre.
* @param {Object} period {deb:Object, fin:Object}
* @param {Object} interval {deb:Object, fin:Object}
* @returns vrai si la periode et l'interval ont une intersection commune
*/
Date.intersect = function (period, interval) {
return period.deb <= interval.fin && period.fin >= interval.deb;
};
2023-11-20 16:55:26 +01:00
Date.removeUTC = function (isoString) {
const reg = new RegExp(/[+-][\d:]+$/);
return isoString.replace(reg, "");
};
Object.defineProperty(Date.prototype, "isValid", {
value: function () {
return !Number.isNaN(this.getTime());
},
});
Object.defineProperty(Date.prototype, "startOf", {
/**
* Génère u la date à la plus petite valeur pour la précision donnée.
* @param {string} precision - La précision souhaitée (year, month, day, hours, minutes, seconds, milliseconds).
* @returns {Date} - Une nouvelle date ajustée.
*/
value: function (precision) {
const newDate = this.clone();
switch (precision) {
case "year":
newDate.setMonth(0);
case "month":
newDate.setDate(1);
case "day":
newDate.setHours(0);
case "hours":
newDate.setMinutes(0);
case "minutes":
newDate.setSeconds(0);
case "seconds":
newDate.setMilliseconds(0);
break;
case "milliseconds":
break;
default:
throw new Error(
`Invalid precision for startOf function [${precision}]`
);
}
return newDate;
},
});
Object.defineProperty(Date.prototype, "endOf", {
/**
* Ajuste la date à la plus grande valeur pour la précision donnée.
* @param {string} precision - La précision souhaitée (year, month, day, hours, minutes, seconds, milliseconds).
* @returns {Date} - Une nouvelle date ajustée.
*/
value: function (precision) {
const newDate = this.clone();
switch (precision) {
case "year":
newDate.setMonth(11); // Décembre est le 11ème mois (0-indexé)
case "month":
newDate.setDate(0); // Le jour 0 du mois suivant est le dernier jour du mois courant
newDate.setMonth(newDate.getMonth() + 1);
case "day":
newDate.setHours(23); // 23 heures est la dernière heure de la journée
case "hours":
newDate.setMinutes(59); // 59 minutes est la dernière minute de l'heure
case "minutes":
newDate.setSeconds(59); // 59 secondes est la dernière seconde de la minute
case "seconds":
newDate.setMilliseconds(999); // 999 millisecondes est la dernière milliseconde de la seconde
break;
case "milliseconds":
// Rien à faire pour les millisecondes
break;
default:
throw new Error("Invalid precision for endOf function");
}
return newDate;
},
});
Object.defineProperty(Date.prototype, "isBefore", {
/**
* Retourne vrai si la date est située avant la date fournie
* @param {Date} date
* @returns {boolean}
*/
value: function (date) {
return this.valueOf() < date.valueOf();
},
});
Object.defineProperty(Date.prototype, "isAfter", {
/**
* Retourne vrai si la date est située après la date fournie
* @param {Date} date
* @returns {boolean}
*/
value: function (date) {
return this.valueOf() > date.valueOf();
},
});
Object.defineProperty(Date.prototype, "isSame", {
/**
* Retourne vrai si les dates sont les mêmes
* @param {Date} date
* @param {string} precision default : "milliseconds"
* @returns boolean
*/
value: function (date, precision = "milliseconds") {
return (
this.startOf(precision).valueOf() == date.startOf(precision).valueOf()
);
},
});
Object.defineProperty(Date.prototype, "isBetween", {
/**
* Vérifie si la date est comprise dans une période avec une précision et une inclusivité optionnelles
* @param {Date} deb - La date de début de la période
* @param {Date} fin - La date de fin de la période
* @param {String} bornes - L'inclusivité/exclusivité de la comparaison ("[]", "()", "[)", "(]")
* - bornes incluses : []
* - bornes excluses : ()
* - borne gauche incluse et borne droit excluse : [)
* - borne gauche excluse et borne droit incluse : (]
*/
value: function (deb, fin, bornes = "[]") {
// Ajuste la date actuelle, la date de début et la date de fin à la précision spécifiée
// Vérifie les bornes en fonction de l'inclusivité/exclusivité spécifiée dans 'bornes'
const check_deb =
bornes[0] === "("
? this.valueOf() > deb.valueOf()
: this.valueOf() >= deb.valueOf();
const check_fin =
bornes[1] === ")"
? fin.valueOf() > this.valueOf()
: fin.valueOf() >= this.valueOf();
return check_deb && check_fin;
},
});
Object.defineProperty(Date.prototype, "toIsoUtcString", {
/**
* @returns date au format iso utc (yyyy-mm-ddThh:MM±oo:oo:oo)
*/
value: function () {
2023-11-20 16:55:26 +01:00
// Formater la date et l'heure
const date = this;
var tzo = -date.getTimezoneOffset(),
dif = tzo >= 0 ? "+" : "-",
pad = function (num) {
return (num < 10 ? "0" : "") + num;
};
2023-11-20 16:55:26 +01:00
return (
this.toFakeIso() +
dif +
pad(Math.floor(Math.abs(tzo) / 60)) +
":" +
pad(Math.abs(tzo) % 60)
);
},
});
2023-11-20 16:55:26 +01:00
Object.defineProperty(Date.prototype, "toFakeIso", {
value: function () {
const date = this;
pad = function (num) {
return (num < 10 ? "0" : "") + num;
};
return (
date.getFullYear() +
"-" +
pad(date.getMonth() + 1) +
"-" +
pad(date.getDate()) +
"T" +
pad(date.getHours()) +
":" +
pad(date.getMinutes()) +
":" +
2023-11-20 16:55:26 +01:00
pad(date.getSeconds())
);
},
});
Object.defineProperty(Date.prototype, "clone", {
/**
* @returns Retourne une copie de la date (copie non liée)
*/
value: function () {
return structuredClone(this);
},
});
Object.defineProperty(Date.prototype, "format", {
value: function (formatString) {
2023-11-15 14:11:47 +01:00
let iso = this.toIsoUtcString();
switch (formatString) {
2024-03-15 15:56:33 +01:00
case "DD/MM/YYYY":
return this.toLocaleString("fr-FR", {
day: "2-digit",
month: "2-digit",
year: "numeric",
timeZone: SCO_TIMEZONE,
});
case "DD/MM/Y HH:mm":
return this.toLocaleString("fr-FR", {
day: "2-digit",
month: "2-digit",
year: "2-digit",
hour: "2-digit",
minute: "2-digit",
hour12: false,
2023-11-20 16:55:26 +01:00
timeZone: SCO_TIMEZONE,
});
case "DD/MM/YYYY HH:mm":
return this.toLocaleString("fr-FR", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
hour12: false,
2023-11-20 16:55:26 +01:00
timeZone: SCO_TIMEZONE,
});
2024-03-15 15:56:33 +01:00
case "HH:mm":
return iso.slice(11, 16);
case "YYYY-MM-DDTHH:mm":
// slice : YYYY-MM-DDTHH
// slice + 3 : YYYY-MM-DDTHH:mm
return iso.slice(0, iso.indexOf(":") + 3);
2023-11-15 14:11:47 +01:00
case "YYYY-MM-DD":
return iso.slice(0, iso.indexOf("T"));
default:
2023-11-20 16:55:26 +01:00
return this.toFakeIso();
}
},
});
Object.defineProperty(Date.prototype, "add", {
/**
* Ajoute une valeur spécifiée à un élément de la date.
* @param {number} value - La valeur à ajouter.
* @param {string} type - Le type de la valeur (year, month, day, hours, minutes, seconds).
*/
value: function (value, type) {
switch (type) {
case "years":
this.setFullYear(this.getFullYear() + value);
break;
case "months":
this.setMonth(this.getMonth() + value);
break;
case "days":
this.setDate(this.getDate() + value);
break;
case "hours":
this.setHours(this.getHours() + value);
break;
case "minutes":
this.setMinutes(this.getMinutes() + value);
break;
case "seconds":
this.setSeconds(this.getSeconds() + value);
break;
default:
throw new Error(
`Invalid type for adding to date | type : ${type} value : ${value}`
);
}
return this; // Return the modified date
},
});
class Duration {
/**
* Constructeur de la classe Duration.
* @param {Date} start - La date de début de la période.
* @param {Date} end - La date de fin de la période.
*/
constructor(start, end) {
this.start = start; // Stocke la date de début.
this.end = end; // Stocke la date de fin.
this.duration = end - start; // Calcule la durée en millisecondes entre les deux dates.
}
/**
* Calcule le nombre d'années entre les deux dates et arrondit le résultat à quatre décimales.
* @return {number} Le nombre d'années arrondi à quatre décimales.
*/
get years() {
const startYear = this.start.getFullYear(); // Obtient l'année de la date de début.
const endYear = this.end.getFullYear(); // Obtient l'année de la date de fin.
// Calcule la différence en années et arrondit à quatre décimales.
return parseFloat((endYear - startYear).toFixed(4));
}
/**
* Calcule le nombre de mois entre les deux dates, en tenant compte des années et des jours, et arrondit le résultat à quatre décimales.
* @return {number} Le nombre de mois arrondi à quatre décimales.
*/
get months() {
const years = this.years; // Nombre d'années complètes.
// Calcule la différence en mois, en ajoutant la différence en jours divisée par 30 pour une approximation.
const months =
years * 12 +
(this.end.getMonth() - this.start.getMonth()) +
(this.end.getDate() - this.start.getDate()) / 30;
// Arrondit à quatre décimales.
return parseFloat(months.toFixed(4));
}
/**
* Calcule le nombre de jours entre les deux dates et arrondit le résultat à quatre décimales.
* @return {number} Le nombre de jours arrondi à quatre décimales.
*/
get days() {
// Convertit la durée en millisecondes en jours et arrondit à quatre décimales.
return parseFloat((this.duration / (24 * 60 * 60 * 1000)).toFixed(4));
}
/**
* Calcule le nombre d'heures entre les deux dates et arrondit le résultat à quatre décimales.
* @return {number} Le nombre d'heures arrondi à quatre décimales.
*/
get hours() {
// Convertit la durée en millisecondes en heures et arrondit à quatre décimales.
return parseFloat((this.duration / (60 * 60 * 1000)).toFixed(4));
}
/**
* Calcule le nombre de minutes entre les deux dates et arrondit le résultat à quatre décimales.
* @return {number} Le nombre de minutes arrondi à quatre décimales.
*/
get minutes() {
// Convertit la durée en millisecondes en minutes et arrondit à quatre décimales.
return parseFloat((this.duration / (60 * 1000)).toFixed(4));
}
/**
* Calcule le nombre de secondes entre les deux dates et arrondit le résultat à quatre décimales.
* @return {number} Le nombre de secondes arrondi à quatre décimales.
*/
get seconds() {
// Convertit la durée en millisecondes en secondes et arrondit à quatre décimales.
return parseFloat((this.duration / 1000).toFixed(4));
}
/**
* Obtient le nombre de millisecondes entre les deux dates et arrondit le résultat à quatre décimales.
* @return {number} Le nombre de millisecondes arrondi à quatre décimales.
*/
get milliseconds() {
// Arrondit la durée totale en millisecondes à quatre décimales.
return parseFloat(this.duration.toFixed(4));
}
}
/**
* Fonction qui vérifie si une période est dans un interval
* Objet période / interval
* {
* deb: Date,
* fin: Date,
* }
* @param {object} period
* @param {object} interval
* @returns {boolean} Vrai si la période est dans l'interval
*/
function hasTimeConflict(period, interval) {
return period.deb.isBefore(interval.fin) && period.fin.isAfter(interval.deb);
}
// Fonction auxiliaire pour obtenir le numéro de semaine ISO d'une date donnée
function getISOWeek(date) {
const target = new Date(date.valueOf());
const dayNr = (date.getUTCDay() + 6) % 7;
target.setUTCDate(target.getUTCDate() - dayNr + 3);
const firstThursday = target.valueOf();
target.setUTCMonth(0, 1);
if (target.getUTCDay() !== 4) {
target.setUTCMonth(0, 1 + ((4 - target.getUTCDay() + 7) % 7));
}
return 1 + Math.ceil((firstThursday - target) / 604800000);
}
// Fonction auxiliaire pour obtenir le nombre de semaines ISO dans une année donnée
function getISOWeeksInYear(year) {
const date = new Date(year, 11, 31);
const week = getISOWeek(date);
return week === 1 ? getISOWeek(new Date(year, 11, 24)) : week;
}