"""Service déterministe de calcul de la parité des semaines pour l'agenda théorique. Ce module fournit :class:`WeekParityService`, un service sans état qui détermine si la semaine contenant une date donnée est paire ou impaire, à partir d'une date d'ancrage dont la parité est connue. L'algorithme repose sur le décalage entre les lundis des deux semaines, et non sur les numéros de semaine ISO. """ from __future__ import annotations from datetime import date, timedelta from typing import Literal class WeekParityService: """Service déterministe de calcul de la parité des semaines. La parité d'une semaine est déduite d'une date d'ancrage fournie à la construction : la semaine contenant cette date a une parité connue (paire ou impaire). Le service est immuable après construction et ne dépend d'aucun état global ni de l'horloge système. """ def __init__(self, anchor_date: date, anchor_type: Literal["even", "odd"]) -> None: """Initialise le service avec la date d'ancrage et sa parité. :param anchor_date: Date de référence dont la semaine a une parité connue. :param anchor_type: Parité de la semaine d'ancrage (``"even"`` ou ``"odd"``). """ self._anchor_monday = anchor_date - timedelta(days=anchor_date.weekday()) self._anchor_type = anchor_type def parity_for(self, target_date: date) -> Literal["even", "odd"]: """Détermine la parité de la semaine contenant la date cible. Algorithme : 1. Calculer le lundi de la semaine de la date cible. 2. Utiliser le lundi de la semaine de la date d'ancrage (stocké à l'initialisation). 3. Calculer le nombre de semaines entre les deux lundis : ``(target_monday - anchor_monday).days // 7``. 4. Si le décalage de semaines est pair, la cible a la même parité que l'ancrage. 5. Si le décalage de semaines est impair, la cible a la parité opposée. :param target_date: Date dont il faut déterminer la parité. :return: ``"even"`` ou ``"odd"`` selon la parité de la semaine cible. :rtype: Literal["even", "odd"] """ target_monday = target_date - timedelta(days=target_date.weekday()) week_offset = (target_monday - self._anchor_monday).days // 7 if week_offset % 2 == 0: return self._anchor_type # Inverse la parité. return "odd" if self._anchor_type == "even" else "even"