gigafibre-fsm/services/targo-hub/lib/holidays-qc.js
louispaulb 512c4a5f1b feat(ops): dispatch auto complet + perf Boîte/rapports + fix session
Planificateur « Suggérer » : 4 stratégies (smart / meilleurs d'abord / équilibré /
juste ce qu'il faut), compétences+niveaux par tech (édition inline), niveau requis
par compétence + par job, carte des tournées (1 couleur/tech, domicile→arrêts,
sélecteur de jour), fenêtre de dispatch auj.+demain (dates sélectionnées), règle
week-end + placeholder « en attente du quart », clustering + lasso + filtre-date
sur la carte, accès rapide « À assigner » (badge).

Boîte : liste /conversations allégée (45 Mo → ~1 Mo, 17×) + messages chargés à
l'ouverture. Rapports : cache SWR sur revenue-explorer (22×). Session : keep-alive
+ timeout fetch global + authFetch durci → fin des rechargements manuels.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 08:49:35 -04:00

59 lines
3.5 KiB
JavaScript

// ── Jours fériés statutaires du QUÉBEC — DÉTERMINISTE (pas d'IA : ce sont des règles fixes) ──
// Base = Loi sur les normes du travail (CNESST). 8 fériés. L'employeur peut AJOUTER des observances
// (lendemain, congé mobile…) via `extra`, et choisir Vendredi saint OU Lundi de Pâques.
// ⚠️ Tout en UTC pour éviter toute dérive de fuseau (les dates de fériés sont des jours civils).
// Dimanche de Pâques (algorithme de Meeus/Jones/Butcher).
function easterSunday (year) {
const a = year % 19, b = Math.floor(year / 100), c = year % 100
const d = Math.floor(b / 4), e = b % 4, f = Math.floor((b + 8) / 25)
const g = Math.floor((b - f + 1) / 3), h = (19 * a + b - d - g + 15) % 30
const i = Math.floor(c / 4), k = c % 4, l = (32 + 2 * e + 2 * i - h - k) % 7
const m = Math.floor((a + 11 * h + 22 * l) / 451)
const month = Math.floor((h + l - 7 * m + 114) / 31) // 3 = mars, 4 = avril
const day = ((h + l - 7 * m + 114) % 31) + 1
return new Date(Date.UTC(year, month - 1, day))
}
// n-ième `weekday` (0=dim … 1=lun) du mois (month0 0-based).
function nthWeekday (year, month0, weekday, n) {
const first = new Date(Date.UTC(year, month0, 1))
const day = 1 + ((7 + weekday - first.getUTCDay()) % 7) + (n - 1) * 7
return new Date(Date.UTC(year, month0, day))
}
const iso = (d) => d.toISOString().slice(0, 10)
// Liste des fériés QC pour une année. easterChoice: 'good_friday' (défaut) | 'easter_monday'.
function holidaysForYear (year, { easterChoice = 'good_friday', extra = [] } = {}) {
const easter = easterSunday(year)
const goodFriday = new Date(easter); goodFriday.setUTCDate(easter.getUTCDate() - 2)
const easterMonday = new Date(easter); easterMonday.setUTCDate(easter.getUTCDate() + 1)
// Journée nationale des patriotes = le LUNDI qui précède le 25 mai (donc lundi ≤ 24 mai).
const patriotes = new Date(Date.UTC(year, 4, 24)); while (patriotes.getUTCDay() !== 1) patriotes.setUTCDate(patriotes.getUTCDate() - 1)
const list = [
{ date: iso(new Date(Date.UTC(year, 0, 1))), name: "Jour de l'An" },
{ date: iso(easterChoice === 'easter_monday' ? easterMonday : goodFriday), name: easterChoice === 'easter_monday' ? 'Lundi de Pâques' : 'Vendredi saint' },
{ date: iso(patriotes), name: 'Journée nationale des patriotes' },
{ date: iso(new Date(Date.UTC(year, 5, 24))), name: 'Saint-Jean-Baptiste' },
{ date: iso(new Date(Date.UTC(year, 6, 1))), name: 'Fête du Canada' },
{ date: iso(nthWeekday(year, 8, 1, 1)), name: 'Fête du travail' }, // 1er lundi de septembre
{ date: iso(nthWeekday(year, 9, 1, 2)), name: 'Action de grâce' }, // 2e lundi d'octobre
{ date: iso(new Date(Date.UTC(year, 11, 25))), name: 'Noël' },
]
for (const x of (extra || [])) if (x && x.date) list.push({ date: x.date, name: x.name || 'Congé', custom: true })
return list.sort((p, q) => p.date.localeCompare(q.date))
}
// Fériés entre deux dates ISO (incluses), traversant les années.
function holidaysBetween (fromIso, toIso, opts = {}) {
const y0 = parseInt(String(fromIso).slice(0, 4), 10); const y1 = parseInt(String(toIso).slice(0, 4), 10)
const out = []
for (let y = y0; y <= y1; y++) for (const h of holidaysForYear(y, opts)) if (h.date >= fromIso && h.date <= toIso) out.push(h)
return out
}
function isHoliday (dateIso, opts = {}) {
const y = parseInt(String(dateIso).slice(0, 4), 10)
return holidaysForYear(y, opts).find(h => h.date === dateIso) || null
}
module.exports = { holidaysForYear, holidaysBetween, isHoliday, easterSunday }