Python: Jours fériés français

Voici une classe que j'ai développé afin de gérer efficacement les jours fériés français.

Elle pourra être utilisée ultérieurement dans différentes applications.

Voici le code:

from enum import IntEnum
from datetime import datetime, date
from dateutil.easter import easter, EASTER_WESTERN
from dateutil.relativedelta import relativedelta
import json
from collections import namedtuple
import serpy

class Mois(IntEnum):
    
    JANVIER   = 1
    FEVRIER   = 2
    MARS      = 3
    AVRIL     = 4
    MAI       = 5
    JUIN      = 6
    JUILLET   = 7
    AOUT      = 8
    SEPTEMBRE = 9
    OCTOBRE   = 10
    NOVEMBRE  = 11
    DECEMBRE  = 12
            
class JoursFeries(object):
    
    def __init__(self, annee: int = datetime.now().year):
        self.CURRENT_YEAR = int(annee)
            
    @property
    def CURRENT_YEAR(self) -> int:
        return self._annee

    @CURRENT_YEAR.setter
    def CURRENT_YEAR(self, value: int) -> None:
        self._annee = value
    
    @property
    def JOUR_DE_L_AN(self) -> date:
        return date(self.CURRENT_YEAR, Mois.JANVIER, 1)
    
    @property
    def PAQUES(self) -> date:
        return easter(self.CURRENT_YEAR, method=EASTER_WESTERN)
    
    @property
    def LUNDI_DE_PAQUES(self) -> date:
        return self.PAQUES + relativedelta(days=1)
    
    @property
    def FETE_DU_TRAVAIL(self) -> date:
        return date(self.CURRENT_YEAR, Mois.MAI, 1)
    
    @property
    def VICTOIRE_1945(self) -> date:
        return date(self.CURRENT_YEAR, Mois.MAI, 8)
    
    @property
    def ASCENSION(self) -> date:
        return self.PAQUES + relativedelta(days=39)
    
    @property
    def PENTECOTE(self) -> date:
        return self.PAQUES + relativedelta(days=49)
    
    @property
    def LUNDI_DE_PENTECOTE(self) -> date:
        return self.PENTECOTE + relativedelta(days=1)
    
    @property
    def FETE_NATIONALE(self) -> date:
        return date(self.CURRENT_YEAR, Mois.JUILLET, 14)
    
    @property
    def ASSOMPTION(self) -> date:
        return date(self.CURRENT_YEAR, Mois.AOUT, 15)
    
    @property
    def TOUSSAINT(self) -> date:
        return date(self.CURRENT_YEAR, Mois.NOVEMBRE, 1)
    
    @property
    def ARMISTICE_1918(self) -> date:
        return date(self.CURRENT_YEAR, Mois.NOVEMBRE, 11)
    
    @property
    def NOEL(self) -> date:
        return date(self.CURRENT_YEAR, Mois.DECEMBRE, 25)
    
    @property
    def proprietes(self) -> list:
        return list(self.dumps().keys())
    
    def to_list(self) -> list:
        return [getattr(self, x) for x in self.dumps().keys()]
    
    def __str__(self) -> str:
        return '\n'.join([f'{x:<20s}: {getattr(self, x)}' for x in self.dumps().keys()])
    
    def __repr__(self) ->str:
        return json.dumps(self.dumps(), indent=4, ensure_ascii=False)
        
    def dumps(self) -> dict:
        return JoursFeriesSerialize(self).data
    
    def to_namedtuple(self) -> namedtuple:
        return namedtuple('JourFeries', self.dumps().keys())(**self.dumps())
    
class JoursFeriesSerialize(serpy.Serializer):
    
    JOUR_DE_L_AN       = serpy.StrField()
    PAQUES             = serpy.StrField()
    LUNDI_DE_PAQUES    = serpy.StrField()
    FETE_DU_TRAVAIL    = serpy.StrField()
    VICTOIRE_1945      = serpy.StrField()
    ASCENSION          = serpy.StrField()
    PENTECOTE          = serpy.StrField()
    LUNDI_DE_PENTECOTE = serpy.StrField()
    FETE_NATIONALE     = serpy.StrField()
    ASSOMPTION         = serpy.StrField()
    TOUSSAINT          = serpy.StrField()
    ARMISTICE_1918     = serpy.StrField()
    NOEL               = serpy.StrField()

La classe Mois énumère tous les mois de l'année grâce au package enum.IntEnum.

La classe JoursFeriesSerialize permet de sérialiser les données de la classe JoursFeries grâce au package serpy.

Pour finir, la classe JoursFeries qui permet de générer tous les jours fériés de l'année en cours ou de celle passée en paramètre au constructeur de la classe.

Le jour férié correspondant à Pâques est récupéré grâce au package dateutil.easter.easter et tous les jours fériés ayant un rapport avec Pâques sont générés grâce au package dateutil.relativedelta.relativedelta.

Pour l'utiliser, rien de plus simple:

>>> jf = JoursFeries()
>>> jf
{
    "JOUR_DE_L_AN": "2022-01-01",
    "PAQUES": "2022-04-17",
    "LUNDI_DE_PAQUES": "2022-04-18",
    "FETE_DU_TRAVAIL": "2022-05-01",
    "VICTOIRE_1945": "2022-05-08",
    "ASCENSION": "2022-05-26",
    "PENTECOTE": "2022-06-05",
    "LUNDI_DE_PENTECOTE": "2022-06-06",
    "FETE_NATIONALE": "2022-07-14",
    "ASSOMPTION": "2022-08-15",
    "TOUSSAINT": "2022-11-01",
    "ARMISTICE_1918": "2022-11-11",
    "NOEL": "2022-12-25"
}

Sans paramètre, la classe retourne tous les jours fériés de l'année en cours.

La méthode __repr__ permet d'afficher les jours fériés au format json (str).

Avec l'année passée en paramètre:

>>> jf = JoursFeries(2024)
>>> jf
{
    "JOUR_DE_L_AN": "2024-01-01",
    "PAQUES": "2024-03-31",
    "LUNDI_DE_PAQUES": "2024-04-01",
    "FETE_DU_TRAVAIL": "2024-05-01",
    "VICTOIRE_1945": "2024-05-08",
    "ASCENSION": "2024-05-09",
    "PENTECOTE": "2024-05-19",
    "LUNDI_DE_PENTECOTE": "2024-05-20",
    "FETE_NATIONALE": "2024-07-14",
    "ASSOMPTION": "2024-08-15",
    "TOUSSAINT": "2024-11-01",
    "ARMISTICE_1918": "2024-11-11",
    "NOEL": "2024-12-25"
}

La méthode to_list retourne une liste contenant tous les jours fériés au format datetime.date

>>> jf.to_list()
[datetime.date(2022, 1, 1),
 datetime.date(2022, 4, 17),
 datetime.date(2022, 4, 18),
 datetime.date(2022, 5, 1),
 datetime.date(2022, 5, 8),
 datetime.date(2022, 5, 26),
 datetime.date(2022, 6, 5),
 datetime.date(2022, 6, 6),
 datetime.date(2022, 7, 14),
 datetime.date(2022, 8, 15),
 datetime.date(2022, 11, 1),
 datetime.date(2022, 11, 11),
 datetime.date(2022, 12, 25)]

Un print de l'objet:

>>> print(jf)
JOUR_DE_L_AN        : 2022-01-01
PAQUES              : 2022-04-17
LUNDI_DE_PAQUES     : 2022-04-18
FETE_DU_TRAVAIL     : 2022-05-01
VICTOIRE_1945       : 2022-05-08
ASCENSION           : 2022-05-26
PENTECOTE           : 2022-06-05
LUNDI_DE_PENTECOTE  : 2022-06-06
FETE_NATIONALE      : 2022-07-14
ASSOMPTION          : 2022-08-15
TOUSSAINT           : 2022-11-01
ARMISTICE_1918      : 2022-11-11
NOEL                : 2022-12-25

La méthode dumps retourne une représentation json (dict)

>>> jf.dumps()
{'JOUR_DE_L_AN': '2022-01-01',
 'PAQUES': '2022-04-17',
 'LUNDI_DE_PAQUES': '2022-04-18',
 'FETE_DU_TRAVAIL': '2022-05-01',
 'VICTOIRE_1945': '2022-05-08',
 'ASCENSION': '2022-05-26',
 'PENTECOTE': '2022-06-05',
 'LUNDI_DE_PENTECOTE': '2022-06-06',
 'FETE_NATIONALE': '2022-07-14',
 'ASSOMPTION': '2022-08-15',
 'TOUSSAINT': '2022-11-01',
 'ARMISTICE_1918': '2022-11-11',
 'NOEL': '2022-12-25'}

La méthode to_namedtuple retourne un objet collections.namedtuple

>>> jf.to_namedtuple()
JourFeries(JOUR_DE_L_AN='2022-01-01', PAQUES='2022-04-17', LUNDI_DE_PAQUES='2022-04-18', FETE_DU_TRAVAIL='2022-05-01', VICTOIRE_1945='2022-05-08', ASCENSION='2022-05-26', PENTECOTE='2022-06-05', LUNDI_DE_PENTECOTE='2022-06-06', FETE_NATIONALE='2022-07-14', ASSOMPTION='2022-08-15', TOUSSAINT='2022-11-01', ARMISTICE_1918='2022-11-11', NOEL='2022-12-25')

Pour afficher le jour férié correspondant à Pâques

>>> jf.PAQUES
datetime.date(2022, 4, 17)
>>> jf.PAQUES.year
2022
>>> jf.PAQUES.day
17
>>> jf.PAQUES.month
4
>>> jf.PAQUES.isocalendar()
(2022, 15, 7) # le 7ème jour de la semaine 15 de l'année 2022
>>> jf.PAQUES.isoformat()
2022-04-17
>>> jf.PAQUES.timetuple()
time.struct_time(tm_year=2022, tm_mon=4, tm_mday=17, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=6, tm_yday=107, tm_isdst=-1)

Tous les jours fériés disposent des mêmes fonctions.

Comme les jours fériés sont au format datetime.date, toutes les méthodes de l'objet datetime.date sont également accessibles.

La méthode proprietes affiche la liste des noms de tous les jours fériés

>>> jf.proprietes
['JOUR_DE_L_AN',
 'PAQUES',
 'LUNDI_DE_PAQUES',
 'FETE_DU_TRAVAIL',
 'VICTOIRE_1945',
 'ASCENSION',
 'PENTECOTE',
 'LUNDI_DE_PENTECOTE',
 'FETE_NATIONALE',
 'ASSOMPTION',
 'TOUSSAINT',
 'ARMISTICE_1918',
 'NOEL']

Ce qui permet de faire ceci

>>> for nom in jf.proprietes:
...    print(f"{nom:<20} {getattr(JoursFeries(dtstart.year), nom).strftime('%a %d %B %Y')}")
JOUR_DE_L_AN         Sat 01 Jan 2022
PAQUES               Sun 17 Apr 2022
LUNDI_DE_PAQUES      Mon 18 Apr 2022
FETE_DU_TRAVAIL      Sun 01 May 2022
VICTOIRE_1945        Sun 08 May 2022
ASCENSION            Thu 26 May 2022
PENTECOTE            Sun 05 Jun 2022
LUNDI_DE_PENTECOTE   Mon 06 Jun 2022
FETE_NATIONALE       Thu 14 Jul 2022
ASSOMPTION           Mon 15 Aug 2022
TOUSSAINT            Tue 01 Nov 2022
ARMISTICE_1918       Fri 11 Nov 2022
NOEL                 Sun 25 Dec 2022

Vraiment super pratique.