11484 sujets

JavaScript, DOM et API Web HTML5

Bonsoir,

En parallèle de 2 ou 3 autres trucs, je continue - sporadiquement - de coder un calendrier liturgique.

J'en viens maintenant à un point (par où j'aurais peut-être dû commencer) qui est la définition des périodes dans mon calendrier : temps ordinaire (par défaut), temps de l'Avent, temps de carême, etc...

J'utilise Luxon comme librairie de date ***. Du coup j'ai utilisé l'api avec Interval qui me retourne deux dates start et end formatées en jour et mois comme je le demande :
2911 – 2412

Ma question : comment itérer là-dessus pour sortir tous les jours de la période ?
Mais peut-être que ma question n'est pas la bonne : ce que je cherche à faire c'est de trouver si un jour courant correspond à l'un des jours du temps de l'Avent, et donc s'il correspond à l'un des jours situés entre les deux dates.

Pour vous montrer quelque chose, j'ai réussi à isoler la partie de mon code concernée et à la reproduire de Node.js à une version front, sur un Pen : CodePen : Luxon Interval

Le code (pour laisser une trace sur le forum) :
const { DateTime, Interval } = luxon,
      dayMonth = DateTime.local().toFormat('ddMM'),
      year = DateTime.local().toFormat('yyyy'),
      sundayBeforeChristmas = DateTime.fromFormat('2512' + year, 'ddMMyyyy').startOf('week'),
      advent = Interval.fromDateTimes(sundayBeforeChristmas.plus({days: -22}), DateTime.fromFormat('2412' + year, 'ddMMyyyy')).toFormat('ddMM')

let data = {}

// Périodes liturgiques (dev' en cours) :
data.period = "Temps ordinaire"
if (dayMonth === advent) data.period = "Temps de l'Avent" // bien sûr ça ne fonctionne pas en l'état, c'est là où je bloque.

console.log(advent) // => 2911 – 2412
console.log(data.period)

Merci pour votre attention.

*** Ben oui, pourquoi faire comme tout le monde et de bénéficier de l'énorme support qu'offre moment.js ? Tout ça pour faire hipster... Smiley biggol Blague mise à part : parce que moment lui-même la sponsorise, que j'ai lu des trucs sur l'immutabilité de cette lib' (mais elle n'est pas la seule) et surtout je la crois proche de la future API native pour js : Temporal.
Modifié par Olivier C (03 Dec 2020 - 01:12)
Hello,

Bon déjà tu es fâché avec les ; Smiley lol
Il suffit de 'splitter' ta période advent pour appliquer une condition


const { DateTime, Interval } = luxon,
      dayMonth = DateTime.local().toFormat('ddMM'),
      year = DateTime.local().toFormat('yyyy'),
      sundayBeforeChristmas = DateTime.fromFormat('2512' + year, 'ddMMyyyy').startOf('week'),
      advent = Interval.fromDateTimes(sundayBeforeChristmas.plus({days: -22}), DateTime.fromFormat('2412' + year, 'ddMMyyyy')).toFormat('ddMM');

let data = {};

// Périodes liturgiques :
// ici on supprime les espaces et on fait un tableau des 2 valeurs
const adventPeriod = advent.replace(/\s/g,"").split("–",2);
const adventStart = adventPeriod[1]; // start = valeur de droite
const adventEnd = adventPeriod[0]; // end = valeur de gauche

// si dayMonth est compris entre start et end
if ( dayMonth >= adventStart && dayMonth <= adventEnd ) {
  
  data.period = "Temps de l'Avent";
  
} else {
  
  data.period = "Temps ordinaire";
  
}

const el = document.querySelector('.output');
el.innerHTML = `${advent}<br>${data.period}`; // el.textContent
Bonjour stryk,

Ouvrons une parenthèse :
stryk a écrit :
Bon déjà tu es fâché avec les ; Smiley lol

Oui, c'est dans ma description :
Olivier C a écrit :
Déteste les chevrons, les points virgules et les doubles quotes.

Blague mise à part, j'ai toujours codé comme ça sous node.js et ça ne m'a encore jamais posé de problème (il parait que ça peut-être le cas dans de rare conditions). Côté front le code est parsé pour être minifié et optimisé de toute façon. Voilà pour la parenthèse.

Pour le sujet en cours : je n'ai pas d'intérêt à spliter sur la période puisque je définis moi-même la plage juste avant, dans ce cas autant prendre directement les valeurs de début et de fin.

D'autre part ton système aurait fonctionné si les dates avaient été dans un ordre de grandeur numérique (codées en 'MMdd'), sauf que c'est l'inverse et du coup ça ne marche pas : ici le 20 décembre ('2012') est plus petit que le 21 décembre ('2111'). Il faut donc convertir ces valeurs en objet date puis les passer dans les conditions. Cela je sais faire :
const { DateTime, Interval } = luxon,
      dayMonth = DateTime.local().toFormat('ddMM'),
      year = DateTime.local().toFormat('yyyy'),
      sundayBeforeChristmas = DateTime.fromFormat('2512' + year, 'ddMMyyyy').startOf('week'),
      firstAdventSunday = sundayBeforeChristmas.plus({days: -22}).toFormat('ddMM'),
      advent = Interval.fromDateTimes(sundayBeforeChristmas.plus({days: -22}), DateTime.fromFormat('2412' + year, 'ddMMyyyy')).toFormat('ddMM')

let data = {}

// Périodes liturgiques :
data.period = "Temps ordinaire"
if (DateTime.fromFormat(dayMonth, 'ddMM') >= DateTime.fromFormat(firstAdventSunday, 'ddMM') && DateTime.fromFormat(dayMonth, 'ddMM') < DateTime.fromFormat('2512', 'ddMM')) data.period = "Temps de l'Avent" // @todo Ce n'est pas encore optimisé.

const el = document.querySelector('.output')
el.innerHTML = `${advent}<br>${data.period}<br>${dayMonth}`

Mais c'est juste que j'avais vu que les lib' de dates proposaient pour la plupart des API pour les intervalles de temps et que je pensais qu'il y avait un moyen d'optimiser tout ça.

En tout cas, merci d'avoir pris le temps d'intervenir.
Modifié par Olivier C (03 Dec 2020 - 12:20)
Ahhhh ok je n'avais pas compris effectivement !!
ça ne me parait pas très catholique tout ça Smiley lol Smiley lol

Sinon, tu as fait le plus compliqué, je ne vois pas où se situe le souci ...
Pour comparer deux dates:

const dateDebut = new Date(2020,10,29); // 29 NOV
const datefin = new Date(2020,11,24); // 24 DEC
const dateActuelle = new Date();

if ( dateActuelle >= dateDebut && dateActuelle <= datefin ) console.log("on y est");


Il suffit de remplacer les dates en dur par tes variables déjà déclarées !
Oui oui, mon code fonctionne, je cherchais juste à comprendre comment le faire d'une autre manière. D'où ma question sur les intervalles. Mais peut être que ce cas d'utilisation est réservé à autre chose...
Cette petite conversation m'a fait réfléchir (comme quoi une petite intervention sur le forum n'est jamais inutile et remet les idées en place lorsqu'on a la tête dans le guidon) : je vais réécrire toute ma fonction avec des objets date plutôt qu'en m'appuyant sur une chaîne de caractère 'ddMM' (ça allège bien le code au passage) :
const { DateTime } = luxon,
      year = DateTime.local().toFormat('yyyy'),
      date = DateTime.local(),
      //date = DateTime.fromFormat('0312' + year, 'ddMMyyyy'), // pour tester
      dayMonth = DateTime.local().toFormat('ddMM'),
      christmas = DateTime.fromFormat('2512' + year, 'ddMMyyyy'),
      sundayBeforeChristmas = christmas.startOf('week'),
      firstAdventSunday = sundayBeforeChristmas.plus({days: -22})

let data = {}

// Périodes liturgiques :
data.period = "Temps ordinaire"
if (date >= firstAdventSunday && date < christmas) data.period = "Temps de l'Avent"

const el = document.querySelector('.output')
el.innerHTML = `${data.period}<br>${date}<br>${christmas}`

Comme ça tout le calendrier s'appuiera sur la même logique, et surtout cela me permettra de prendre en compte les événements qui commencent la veille au soir (par exemple la veillée de Nöel ou celle de Pâques).

Sujet résolu pour ce qui est de la fonctionnalité. Il reste ouvert pour ce qui est de la compréhension de l'intérêt des intervalles proposés dans les librairies de dates.
Modifié par Olivier C (04 Dec 2020 - 16:06)
Hello,

je viens de regarder de plus près la doc luxon, elle inclus bien une méthode contains() pour vérifier si un DateTime est compris dans un Interval.

  contains(dateTime) {
    if (!this.isValid) return false;
    return this.s <= dateTime && this.e > dateTime;
  }

Je n'ai pas réussi à la faire fonctionner sur ton codepen, peut-être un souci de cdn ou de version ??! ( message d'erreur: contains n'est pas une fonction .. )
La syntaxe donnerai ça:

Interval.contains(DateTime) // renvoi true/false

Ce qui effectivement devient intéressant pour ton usage et raccourci le code:
data.period = advent.contains(dayMonth) ? "Temps de l'Avent" : "Temps Ordinaire";


La piste est à creuser, amicalement
Modifié par stryk (04 Dec 2020 - 12:25)
Meilleure solution
Merci pour ton retour, je vais regarder ça.

Édit : et bien voilà, je crois que tu as trouvé ! Je ne suis pas en mesure de tester maintenant, mais je viens de relire la doc plus précisément après ta remarque et apriori c'est bien cela. Si ça ne marche pas sur le codepen, c'est parce que le module Interval n'est plus chargé : je l'avais retiré entre temps. Je testerai tout cela ce soir et ferai un retour. Merci à toi.
Modifié par Olivier C (04 Dec 2020 - 18:52)
Génial ça marche ! J'avais deux minutes devant moi et j'ai testé :
const { DateTime, Interval } = luxon // le module à charger en plus...
// du code...
if (advent.contains(date)) data.period = "Temps de l'Avent"

Ça va grandement me simplifier la vie pour les calculs compliqués.

Merci beaucoup stryk. Bonne soirée.
Ah top Smiley ravi

Wikipédia a écrit :

Solennité de Saint Joseph, époux de la Vierge Marie : 19 mars, reporté au 20 mars si le 19 tombe un dimanche de carême, ou avancé au samedi avant les Rameaux si le 19 tombe pendant la semaine sainte.

Je pense que les calculs pour la navette Apollo 11 étaient plus simple Smiley lol Smiley lol

Content d'avoir pu t'aider Smiley smile
stryk a écrit :
Je pense que les calculs pour la navette Apollo 11 étaient plus simple Smiley lol

Sans oublier la version longue de cette formule :
Wikipédia v2 a écrit :
Si la fête tombe un dimanche, autre que le Dimanche des Rameaux, celle-ci est célébrée le jour suivant, généralement le lundi 20 mars, mais seulement si une autre solennité (par exemple, un autre Saint patron de l'Église) n'est pas célébrée durant cette journée. Depuis 2008, si le jour de la Fête de Saint Joseph tombe pendant la Semaine Sainte, la célébration de sa fête est déplacée vers le jour le plus proche possible avant le 19 mars, généralement le samedi précédant la Semaine Sainte.

Quand à la date de Pâques l'algorithme est démentiel :
const gregorian = year => {
  const a = year % 19
  const b = integerDivision(year, 100)
  const c = year % 100
  const d = integerDivision(b, 4)
  const e = b % 4
  const f = integerDivision(b + 8, 25)
  const g = integerDivision(b - f + 1, 3)
  const h = (19 * a + b - d - g + 15) % 30
  const i = integerDivision(c, 4)
  const k = c % 4
  const l = (32 + 2 * e + 2 * i - h - k) % 7
  const m = integerDivision(a + 11 * h + 22 * l, 451)
  const month = integerDivision(h + l - 7 * m + 114, 31)
  const day = ((h + l - 7 * m + 114) % 31) + 1

  //return new Date(year, month - 1, day)
  //return new Date(year, month - 1, day + 1)
  return parseInt(('0' + day).slice(-2) + ('0' + month).slice(-2), 10)
}

Mais pour ce dernier calcul j'utilise finalement un module npm.
Et bien entendu le calendrier grégorien n'est qu'un parmi d'autres. Il y a aussi un algorithme (plus simple) pour trouver la date de Pâques dans le calendrier julien (mais qui n'est pas valable pour tous les orthodoxes) :
const julian = year => {
  const a = year % 4
  const b = year % 7
  const c = year % 19
  const d = (19 * c + 15) % 30
  const e = (2 * a + 4 * b - d + 34) % 7
  const month = integerDivision(d + e + 114, 31)
  const day = ((d + e + 114) % 31) + 1

  //return new Date(year, month - 1, day + 13)
  return parseInt(('0' + day).slice(-2) + ('0' + month).slice(-2), 10) // ajouter 13 jours pour la date Orthodoxe basée sur le nouveau calendrier
}
stryk a écrit :
Je sais maintenant pourquoi je suis athée Smiley lol

Ah, ah ! Et ta galette des rois : tu crois que tu peux la manger le 6 janvier pépère ? Et ben non ! Du moins pas en France :
epiphany = DateTime.fromFormat('0201' + year, 'ddMMyyyy').endOf('week')

Modifié par Olivier C (06 Dec 2020 - 11:24)
Déjà tu peux écrire les algo Smiley biggrin

Pour l'islam ça devient vite plus compliqué avec nuit du doute pour déterminer si on aperçoit le croissant de lune Smiley lol