Bonjour à tous,
voici ici un script SQL d’une fonction table qui génère un calendier annuel comportant les jours fériés nationaux et facultativement régionaux (ultra marins et alsace moselle )
CREATE FUNCTION CALENDRIER_ANNUEL (
ANNEE INTEGER default null ,
DEPARTEMENT varchar(3) DEFAULT NULL
)
RETURNS TABLE (
DATE_JOUR DATE ,
JOUR_FR VARCHAR(20) ,
MOIS_FR VARCHAR(20) ,
SEMAINE_ISO INTEGER ,
JOUR_OUVRE BOOLEAN ,
JOUR_OUVRABLE BOOLEAN ,
JOUR_FERIE BOOLEAN ,
NOM_FERIE VARCHAR(40)
)
LANGUAGE SQL
SPECIFIC CALREG
DETERMINISTIC
READS SQL DATA
CALLED ON NULL INPUT
SET OPTION ALWBLK = *ALLREAD ,
ALWCPYDTA = *OPTIMIZE ,
COMMIT = *NONE ,
DBGVIEW = *SOURCE ,
DECRESULT = (31, 31, 00) ,
DYNDFTCOL = *NO ,
DYNUSRPRF = *USER ,
SRTSEQ = *HEX
BEGIN
DECLARE A INTEGER ;
DECLARE B INTEGER ;
DECLARE C INTEGER ;
DECLARE D INTEGER ;
DECLARE E INTEGER ;
DECLARE F INTEGER ;
DECLARE G INTEGER ;
DECLARE H INTEGER ;
DECLARE I INTEGER ;
DECLARE K INTEGER ;
DECLARE L INTEGER ;
DECLARE M INTEGER ;
DECLARE MOIS_PAQUES INTEGER ;
DECLARE JOUR_PAQUES INTEGER ;
DECLARE DATE_PAQUES DATE ;
DECLARE CANNEE CHAR ( 4 ) ;
declare wannee integer;
--vérification de l'année, si elle est nulle on prend l'année courante
set wannee = coalesce(annee, year(current_date));
--calcul du jour de pâques
-- selon l'algorithme de Butcher Meeus
if ( wannee < 1583 or wannee > 9999 ) THEN
signal sqlstate '75001'
set message_text = 'L''année doit être comprise entre 1583 et 9999' ;
END IF ;
SET CANNEE = CHAR ( TRIM ( wANNEE ) ) ;
-- l'algo du calcul du jour de pâques est décrit
-- sur https://fr.wikipedia.org/wiki/Calcul_de_la_date_de_P%C3%A2ques#Algorithme_de_Butcher-Meeus
SET A = MOD ( wANNEE , 19 ) ; --cycle méton
SET B = wANNEE / 100 ;
SET C = MOD ( wANNEE , 100 ) ;
SET D = B / 4 ;
SET E = MOD ( B , 4 ) ;
SET F = ( B + 8 ) / 25 ; --cycle proemptose
SET G = ( B - F + 1 ) / 3 ; --proemptose
SET H = MOD ( ( 19 * A ) + B - D - G + 15 , 30 ) ; --epacte
SET I = C / 4 ;
SET K = MOD ( C , 4 ) ;
SET L = MOD ( 32 + ( 2 * E ) + ( 2 * I ) - H - K , 7 ) ; --lettre dominicale
SET M = ( A + ( 11 * H ) + ( 22 * L ) ) / 451 ; --correction
SET MOIS_PAQUES = ( H + L - ( 7 * M ) + 114 ) / 31 ; --mois de Pâques
SET JOUR_PAQUES = MOD ( H + L - ( 7 * M ) + 114 , 31 ) + 1 ;
SET DATE_PAQUES = DATE (
CANNEE CONCAT
'-' CONCAT
RIGHT ( '0' CONCAT TRIM ( CHAR ( MOIS_PAQUES ) ) , 2 ) CONCAT
'-' CONCAT
RIGHT ( '0' CONCAT TRIM ( CHAR ( JOUR_PAQUES ) ) , 2 )
) ;
RETURN
WITH RECURSIVE
-- jours fériés nationaux
FERIES_NATIONAUX (D, NOMF, PRIORITE) AS (
SELECT V.D, V.NOMF, 1
FROM (
VALUES
(DATE(CANNEE CONCAT '-01-01'), 'Jour de l''an'),
(DATE(CANNEE CONCAT '-05-01'), 'Fête du Travail'),
(DATE(CANNEE CONCAT '-05-08'), 'Victoire 1945'),
(DATE(CANNEE CONCAT '-07-14'), 'Fête Nationale'),
(DATE(CANNEE CONCAT '-08-15'), 'Assomption'),
(DATE(CANNEE CONCAT '-11-01'), 'Toussaint'),
(DATE(CANNEE CONCAT '-11-11'), 'Armistice'),
(DATE(CANNEE CONCAT '-12-25'), 'Noël'),
(DATE_PAQUES + 1 DAY, 'Lundi de Pâques'),
(DATE_PAQUES + 39 DAYS, 'Ascension'),
(DATE_PAQUES + 49 DAYS, 'Pentecôte'),
(DATE_PAQUES + 50 DAYS, 'Lundi de Pentecôte')
) AS V(D, NOMF)
),
-- jours fériés régionaux ultraarins et alsace/moselle
FERIES_REGIONAUX (DEPT, D, NOMF) AS (
VALUES
('971', DATE(CANNEE CONCAT '-05-27'), 'Abolition de l''esclavage'),
('972', DATE(CANNEE CONCAT '-05-22'), 'Abolition de l''esclavage'),
('973', DATE(CANNEE CONCAT '-06-10'), 'Abolition de l''esclavage'),
('974', DATE(CANNEE CONCAT '-12-20'), 'Abolition de l''esclavage'),
('976', DATE(CANNEE CONCAT '-04-27'), 'Abolition de l''esclavage'),
('57', DATE(CANNEE CONCAT '-12-26'), 'Saint-Étienne'),
('67', DATE(CANNEE CONCAT '-12-26'), 'Saint-Étienne'),
('68', DATE(CANNEE CONCAT '-12-26'), 'Saint-Étienne'),
('57', DATE_PAQUES - 2 DAYS, 'Vendredi saint'),
('67', DATE_PAQUES - 2 DAYS, 'Vendredi saint'),
('68', DATE_PAQUES - 2 DAYS, 'Vendredi saint')
),
-- allez!, on mélange le tout
FERIES_TOUT (D, NOMF, PRIORITE) AS (
SELECT D, NOMF, PRIORITE
FROM FERIES_NATIONAUX
UNION ALL
SELECT D, NOMF, 2
FROM FERIES_REGIONAUX
WHERE DEPT = DEPARTEMENT
),
--classement (au cas où un jf régional tombe le même
-- jour qu'un jf national, le national est prioritaire)
FERIES_CLASSES (D, NOMF, RN) AS (
SELECT D, NOMF,
ROW_NUMBER() OVER (
PARTITION BY D
ORDER BY PRIORITE, NOMF
)
FROM FERIES_TOUT
),
FERIES (D, NOMF) AS (
SELECT D, NOMF
FROM FERIES_CLASSES
WHERE RN = 1
),
-- on génère la liste des jours de l'année
DATES (D) AS (
SELECT DATE(CANNEE CONCAT '-01-01')
FROM SYSIBM.SYSDUMMY1
UNION ALL
SELECT D + 1 DAY
FROM DATES
WHERE D < DATE(CANNEE CONCAT '-12-31')
)
--maintenant select final...
SELECT
D . D AS DATE_JOUR ,
CASE DAYOFWEEK ( D . D )
WHEN 1 THEN 'Dimanche'
WHEN 2 THEN 'Lundi'
WHEN 3 THEN 'Mardi'
WHEN 4 THEN 'Mercredi'
WHEN 5 THEN 'Jeudi'
WHEN 6 THEN 'Vendredi'
WHEN 7 THEN 'Samedi'
END AS JOUR_FR ,
CASE MONTH ( D . D )
WHEN 1 THEN 'Janvier'
WHEN 2 THEN 'Février'
WHEN 3 THEN 'Mars'
WHEN 4 THEN 'Avril'
WHEN 5 THEN 'Mai'
WHEN 6 THEN 'Juin'
WHEN 7 THEN 'Juillet'
WHEN 8 THEN 'Août'
WHEN 9 THEN 'Septembre'
WHEN 10 THEN 'Octobre'
WHEN 11 THEN 'Novembre'
WHEN 12 THEN 'Décembre'
END AS MOIS_FR ,
WEEK_ISO ( D . D ) AS SEMAINE_ISO ,
CASE
WHEN DAYOFWEEK ( D . D ) BETWEEN 2 AND 6
AND F . NOMF IS NULL
THEN TRUE ELSE FALSE
END AS JOUR_OUVRE ,
CASE
WHEN DAYOFWEEK ( D . D ) BETWEEN 2 AND 7
AND F . NOMF IS NULL
THEN TRUE ELSE FALSE
END AS JOUR_OUVRABLE ,
CASE WHEN F . NOMF IS NOT NULL
THEN TRUE ELSE FALSE
END AS JOUR_FERIE ,
COALESCE ( F . NOMF , '' ) AS NOM_FERIE
FROM DATES D
LEFT JOIN FERIES F ON F . D = D . D
ORDER BY D . D
;
END ;
cette fonction renvoie 1 tuple par jour d’une année donnée (par défaut l’année en cours), et facultativement dun département donné, avec le nom du jour, le nom du mois, la semaine iso, jour ouvrable, jour ouvré, jour férié.
une fois créée, utilisez la fonction :
select * from table(calendrier_annuel()); ou select * from table(calendrier_annuel(2027, '971'));
Merci à Bob(i) pour avoir vérifié et corrigé quelques erreurs dans le script original.
N’hésitez pas à faire comme lui…
