Déplacement de dates

En fonction de vos besoins métier, il peut être souhaitable de créer une chronologie basée sur une autre, dans laquelle les dates de changement de valeur de la chronologie obtenue sont différentes de celles de la chronologie d'entrée.

CER n'inclut pas d'expressions de déplacement de date, car les types de déplacement de date requis ont tendance à être spécifiques au domaine d'activité. L'approche recommandée consiste à créer une méthode Java statique pour créer la chronologie souhaitée et à appeler cette méthode statique à partir des règles à l'aide de l'expression call.

Important : Lorsque vous implémentez un algorithme de déplacement de date, vous devez vérifier qu'il n'existe aucune tentative de création d'une chronologie avec plusieurs valeurs à une date donnée, car ce type de tentative aboutit à un échec lors de l'exécution.

Les tests de votre algorithme doivent inclure tous les cas particuliers, tels que les années bissextiles ou les mois qui comportent un nombre de jours différent.

Exemple d'ajout de date

Votre besoin métier est le suivant : une personne n'est pas autorisée à demander un type de prestation dans les trois mois suivant la réception de ces mêmes prestations.

Pour implémenter cette exigence métier, vous disposez déjà d'une chronologie isReceivingBenefitTimeline qui indique les périodes pendant lesquelles une personne reçoit des prestations.

Vous avez désormais besoin d'une autre chronologie isDisallowedFromApplyingForBenefitTimeline qui indique les périodes pendant lesquelles toute nouvelle demande de prestations de la part de cette personne n'est pas reçevable. Cette chronologie représente un ajout de date de 3 mois aux dates de changement de valeur dans isReceivingBenefitTimeline :

Figure 1. Exigence liée à une chronologie d'ajout de dateExemple de chronologie.

Voici un exemple d'implémentation de méthode statique qui peut être appelée à partir de règles CER :

package curam.creole.example;

import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import curam.creole.execution.session.Session;
import curam.creole.value.Interval;
import curam.creole.value.Timeline;
import curam.util.type.Date;

public class DateAdditionTimeline {

  /**
   * Crée une chronologie en fonction de la chronologie d'entrée, avec la date
   * décalée du nombre de mois indiqué.    * <p>
   * Notez que le paramètre de la chronologie peut être de n'importe quel type.    *
   * @param session
   * la session CER
   * @param inputTimeline
   * la chronologie dont les dates doivent être décalées
   * @param monthsToAdd
   * le nombre de mois à ajouter à la chronologie de changement
   *          dates
   * @param <VALUE>
   * le type de valeur conservée dans les chronologies d'entrée-sortie
   * @return une nouvelle chronologie avec les valeurs de la chronologie
   * d'entrée, décalée du nombre de mois indiqué.    */
  public static <VALUE> Timeline<VALUE> addMonthsTimeline(
      final Session session, final Timeline<VALUE> inputTimeline,
      final Number monthsToAdd) {

    /*
     * CER transmet généralement un numéro, qui doit être converti en      * nombre entier      */
    final int monthsToAddInteger = monthsToAdd.intValue();

    /*
     * Rechercher les intervalles dans la chronologie d'entrée      */
    final List<? extends Interval<VALUE>> inputIntervals =
        inputTimeline.intervals();

    /*
     * Cumuler les intervalles de sortie. Notez que le mappage s'effectue par date de début,      * car lorsque vous ajoutez des mois, il est possible de décaler plusieurs      * dates d'entrée à la même date de sortie.
     *
     * Par exemple, pour calculer 3 mois après ces dates : 2002-11-28,      * 2002-11-29, 2002-11-30, on obtient dans tous les cas 2003-02-28
     *
     * Dans ce cas, vous devez utiliser la valeur de la première date d'entrée      * uniquement - les dates d'entrée sont traitées dans l'ordre croissant      */
    final Map<Date, Interval<VALUE>> outputIntervalsMap =
        new HashMap<Date, Interval<VALUE>>(inputIntervals.size());

    for (final Interval<VALUE> inputInterval : inputIntervals) {
      // obtenir la date de début d'intervalle
      final Date inputStartDate = inputInterval.startDate();

      /*
       * Ajouter le nombre de mois - mais n mois après le début de la        * durée correspond toujours au début de la durée        */

      final Date outputStartDate;
      if (inputStartDate == null) {
        outputStartDate = null;
      } else {
        final Calendar startDateCalendar =
            inputStartDate.getCalendar();

        startDateCalendar.add(Calendar.MONTH, monthsToAddInteger);
        outputStartDate = new Date(startDateCalendar);

      }

      // vérifier que cette date de sortie n'a pas encore été traitée
      if (!outputIntervalsMap.containsKey(outputStartDate)) {

        /*
         * l'intervalle de sortie utilise la même valeur que l'intervalle          * d'entrée, mais avec une date de début décalée          */

        final Interval<VALUE> outputInterval =
            new Interval<VALUE>(outputStartDate,
                inputInterval.value());
        outputIntervalsMap.put(outputStartDate, outputInterval);
      }
    }

    // créer une chronologie à partir des intervalles de sortie     final Collection<Interval<VALUE>> outputIntervals =
        outputIntervalsMap.values();
    final Timeline<VALUE> outputTimeline =
        new Timeline<VALUE>(outputIntervals);
    return outputTimeline;

  }
}

Exemple d'extension de dates

Votre exigence métier est la suivante : une voiture doit être taxée chaque mois où elle "effectue des trajets", sur un ou plusieurs jours de ce mois. 1.

Pour implémenter cette exigence métier, vous disposez déjà d'une chronologie isOnRoadTimeline qui indique les périodes pendant lesquelles une voiture "effectue des trajets".

Vous avez désormais besoin d'une autre chronologie taxDueTimeline qui indique les périodes pour lesquelles la voiture doit être taxée. Cette chronologie est une extension des dates comprises dans isOnRoadTimeline :

Figure 2. Exigence liée à une chronologie d'extension de datesExemple de chronologie.

Voici un exemple d'implémentation de méthode statique qui peut être appelée à partir de règles CER :

package curam.creole.example;

import java.util.Calendar;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import curam.creole.execution.session.Session;
import curam.creole.value.Interval;
import curam.creole.value.Timeline;
import curam.util.type.Date;

public class DateSpreadingTimeline {

  /**
   * Crée une chronologie de la période pour laquelle une voiture doit être    * taxée.
   * <p>
   * La voiture doit être taxée pour le mois complet, pour n'importe quel mois où    * elle effectue des trajets sur un ou plusieurs jours de ce    * mois.
   */
  public static Timeline<Boolean> taxDue(final Session session,
      final Timeline<Boolean> isOnRoadTimeline) {

    /*
     * Rechercher les intervalles dans la chronologie d'entrée      */
    final List<? extends Interval<Boolean>> isOnRoadIntervals =
        isOnRoadTimeline.intervals();

    /*
     * Cumuler les intervalles de sortie. Notez que le mappage s'effectue par date de début ;
     * il se peut qu'une voiture n'effectue aucun trajet pendant un mois, ce qui signifie
     * qu'aucune taxe n'est requise au début du mois suivant, sauf si
     * elle effectue d'autres trajets au cours du mois suivant. Dans ce cas,
     * le versement d'une taxe est finalement requis.
     *
     * Par exemple, la voiture effectue un nouveau trajet le 2001-01-15, la taxe est donc
     * requise (rétrospectivement) à partir du 2001-01-01.
     *
     * Le 2001-01-24, la voiture n'effectue pas de trajet, il est donc
     * possible qu'aucune taxe ne soit imposée à partir du
     * 2001-02-01.
     *
     * Cependant, le 2001-02-05 la voiture effectue un trajet,
     * elle sera donc finalement taxée à partir du 2001-02-01. La
     * chronologie obtenue fusionne ces périodes pour indiquer que la
     * voiture doit être taxée à partir du 2001-01-01 (en couvrant également la période
     * débutant le 2001-02-01).
     */
    final Map<Date, Interval<Boolean>> taxDueIntervalsMap =
        new HashMap<Date, Interval<Boolean>>(
            isOnRoadIntervals.size());

    for (final Interval<Boolean> isOnRoadInterval :
 isOnRoadIntervals) {
      // obtenir la date de début d'intervalle
      final Date isOnRoadStartDate = isOnRoadInterval.startDate();

      if (isOnRoadStartDate == null) {
        // au début de la durée, la voiture doit être taxée si elle effectue
        // des trajets
        taxDueIntervalsMap.put(null, new Interval<Boolean>(null,
            isOnRoadInterval.value()));
      } else if (isOnRoadInterval.value()) {
        /*
         * début d'une période de trajet de la voiture - la voiture
         * doit être taxée à partir du début du mois contenant le
         * début de cette période          */

        final Calendar carOnRoadStartCalendar =
            isOnRoadStartDate.getCalendar();
        final Calendar startOfMonthCalendar =
            new GregorianCalendar(
                carOnRoadStartCalendar.get(Calendar.YEAR),
                carOnRoadStartCalendar.get(Calendar.MONTH), 1);
        final Date startOfMonthDate =
            new Date(startOfMonthCalendar);

        /*
         * Ajouter à la mappe de périodes de taxe dûe - notez que cela exclura
         * de la mappe tout intervalle de taxe non dûe
         * ajouté théoriquement si la voiture n'a effectué aucun trajet pendant
         * le mois précédent          */
        taxDueIntervalsMap.put(startOfMonthDate,
            new Interval<Boolean>(startOfMonthDate, true));
      } else {
        /*
         * Début d'une période sans trajet de la voiture -
         * supposer que depuis le début du mois suivant, la voiture
         * n'est soumise à aucune taxe. Cette spéculation est maintenue sauf
         * s'il s'avère par la suite que la voiture effectue d'autres trajets
         * le mois suivant, auquel cas cette spéculation est
         * annulée (c'est-à-dire exclue de la mappe).
         */

        final Calendar carOffRoadStartCalendar =
            isOnRoadStartDate.getCalendar();
        final Calendar startOfNextMonthCalendar =
            new GregorianCalendar(
                carOffRoadStartCalendar.get(Calendar.YEAR),
                carOffRoadStartCalendar.get(Calendar.MONTH), 1);
        startOfNextMonthCalendar.add(Calendar.MONTH, 1);

        final Date startOfNextMonthDate =
            new Date(startOfNextMonthCalendar);

        /*
         * Ajouter à la mappe de périodes de taxe dûe - notez que cela exclura
         * de la mappe tout intervalle de taxe non dûe
         * ajouté théoriquement si la voiture n'a effectué aucun trajet pendant
         * le mois précédent          */
        taxDueIntervalsMap.put(startOfNextMonthDate,
            new Interval<Boolean>(startOfNextMonthDate, false));
      }

    }

    // créer une chronologie à partir des intervalles de taxe dûe
    final Collection<Interval<Boolean>> taxDueIntervals =
        taxDueIntervalsMap.values();
    final Timeline<Boolean> taxDueTimeline =
        new Timeline<Boolean>(taxDueIntervals);
    return taxDueTimeline;

  }
}
1 Cela signifie que si une voiture effectue d'autres trajets au cours d'un mois, le propriétaire de la voiture doit s'assurer que la taxe est payée rétrospectivement pour le mois complet.