2 votes

La façon la plus simple d'obtenir des temps qui se chevauchent

Je suis face à une validation qui me fait fumer la tête assez souvent. J'ai un objet que j'appelle Downtime pour l'instant et ça ressemble à ça :

public class Downtime {

  /** The start date of the downtime. */
  private ZonedDateTime downtimeFrom;

  /** The end date of the downtime. */
  private ZonedDateTime downtimeTo;

  /**
   * Gets the downtime from.
   *
   * @return the downtime from
   */
  public ZonedDateTime getDowntimeFrom()
  {
    return downtimeFrom;
  }

  /**
   * Gets the downtime to.
   *
   * @return the downtime to
   */
  public ZonedDateTime getDowntimeTo()
  {
    return downtimeTo;
  }

  /**
   * Sets the downtime from.
   *
   * @param downtimeFrom the new downtime from
   */
  protected void setDowntimeFrom( ZonedDateTime downtimeFrom )
  {
    this.downtimeFrom = downtimeFrom;
  }

  /**
   * Sets the downtime to.
   *
   * @param downtimeTo the new downtime to
   */
  protected void setDowntimeTo( ZonedDateTime downtimeTo )
  {
    this.downtimeTo = downtimeTo;
  }
}

Lorsque je crée un nouveau temps d'arrêt sur une implémentation CRUD, je valide déjà que l'heure de début est bien avant l'heure de fin et d'autres choses encore.
Maintenant, je dois ajouter la validation que lorsque je crée un nouveau temps d'arrêt, il n'interfère pas avec les temps d'arrêt déjà créés. En d'autres termes, la date de début du nouveau temps d'arrêt n'est pas déjà existante. et n'est pas sur un autre temps d'arrêt. ( entre le début et la fin d'un temps d'arrêt déjà créé ).

Ainsi, ma façon de procéder pour l'instant, puisque je ne suis pas doué pour les questions de date et d'heure en matière de localisation, serait la suivante :

private boolean isNewDowntimeValid(Downtime newDowntime, List<Downtime> createdDowntimes){
  // let's just assume I already filtered out that the list only contains the same day. That's actually pretty easy.
  List<ZonedDateTime> dateRange = new LinkedList<>();
  ZonedDateTime newTime = newDowntime.getDowntimeFrom();

  for(Downtime downtime : createdDowntimes){
    ZonedDateTime downtimeStart = downtime.getDowntimeFrom();
    ZonedDateTime downtimeEnd = downtime.getDowntimeTo();

    for(ZonedDateTime start = downtimeStart; !start.isAfter(downtimeEnd); start = start.plusHours(1)){
      dateRange.add(start);
    }
  }
  if(dateRange.contains(newTime)){
    return false;
  }
  return true;
}

Le code est écrit dans ma tête ici, il peut donc y avoir des erreurs de syntaxe mais je pense que vous pouvez avoir une idée de ce que je veux.

Et maintenant, ma question.
Ce code ci-dessus semble être une telle surcharge et je voudrais savoir comment je peux le valider plus rapidement et avec moins de code.

EDIT : Laissez-moi vous donner un exemple clair

J'ai une liste de temps d'arrêt comme celle-ci :

List<Downtime> createdDowntimes = [
{
  start:2015-01-10T00:00Z,
  end:2015-01-10T02:00Z
},
{
  start:2015-01-10T04:00Z,
  end:2015-01-10T06:00Z
},
{
  start:2015-01-10T07:00Z,
  end:2015-01-10T09:00Z
}
]

et ensuite j'ai le nouveau temps d'arrêt que je veux créer :

Downtime newDowntime = 
{ 
  start:2015-01-10T05:00Z,
  end:2015-01-10T05:30Z
}

Dans cet exemple, le nouveau temps d'arrêt est pas valable puisqu'il se situe en fait pendant une période qui est déjà dans un autre temps d'arrêt déjà créé.

J'espère que cela rendra les choses plus claires.

EDIT 2 : Bien que le duplicata marqué comprenne la raison et offre une solution, je veux aussi donner des crédits à Hugo qui a fourni une bonne réponse en tenant compte de mes critères.

Voici une autre solution que j'ai préparée et qui offre un traitement beaucoup plus détaillé des exceptions et des informations.

/*
 * Collision 1 = the new downtime starts before the created ones but ends in their span
 * Collision 2 = the new downtime starts after created ones and also ends after their span
 * Collision 3 = the new downtime starts after created ones and ends in their span
 */
List<Downtime> collision1 = createdDowntimes.stream().filter( e -> e.getDowntimeFrom().isAfter( newTimeStart ) )
    .filter( e -> e.getDowntimeTo().isAfter( newTimeEnd ) ).collect( Collectors.toList() );

List<Downtime> collision2 = createdDowntimes.stream().filter( e -> e.getDowntimeFrom().isBefore( newTimeStart ) )
    .filter( e -> e.getDowntimeTo().isBefore( newTimeEnd ) ).collect( Collectors.toList() );

List<Downtime> collision3 = createdDowntimes.stream().filter( e -> e.getDowntimeFrom().isBefore( newTimeStart ) )
    .filter( e -> e.getDowntimeTo().isAfter( newTimeEnd ) ).collect( Collectors.toList() );

Gardez à l'esprit que ma "solution" est une solution parmi d'autres et qu'elle est également assez intensive en termes de performances puisque les flux sont des opérations lourdes. Donc, si vous n'avez pas besoin de savoir exactement combien sont entrés en collision et pourquoi ils sont entrés en collision, considérez la réponse d'Hugo.

1voto

Pelocho Points 4397

Vérifiez cette question

Ou gardez ceci à l'esprit : deux périodes se chevauchent si et seulement si

(DébutA <= FinB) et (FinA >= DébutB)

Vous devez donc vérifier votre nouvelle Downtime contre tous les précédents que vous avez

1voto

Considérant que :

Cela signifie que la date de début du nouveau temps d'arrêt n'existe pas déjà et ne se trouve pas dans un autre temps d'arrêt. (entre le début et la fin d'un temps d'arrêt déjà créé).

Dans ce cas, vous devez comparer les startDate ( downtimeFrom ) de la nouvelle Downtime avec tous les existants Downtime 's :

private boolean isNewDowntimeValid(Downtime newDowntime, List<Downtime> createdDowntimes) {
    // the start of the new downtime
    ZonedDateTime newStartTime = newDowntime.getDowntimeFrom();

    for (Downtime downtime : createdDowntimes) {
        ZonedDateTime downtimeStart = downtime.getDowntimeFrom();
        ZonedDateTime downtimeEnd = downtime.getDowntimeTo();

        if (newStartTime.equals(downtimeStart)) {
            // start date of new downtime already exists
            return false;
        }

        // start date of new downtime is in the existent downtime
        // (existent startDate < new startDate and new startDate < existent endDate)
        if (downtimeStart.isBefore(newStartTime) && newStartTime.isBefore(downtimeEnd)) {
            return false;
        }
    }

    // no invalid cases found, it's valid
    return true;
}

Note : Dans ce code, le Downtime es valide si le nouveau startDate es est égal à la fin d'un existant Downtime . Si vous ne voulez pas cela, vous pouvez changer le deuxième élément de la liste. if à :

if (downtimeStart.isBefore(newStartTime) && (! newStartTime.isAfter(downtimeEnd))) {
    return false;
}

0voto

Supposez que vous devez Heure locale objets,

finA représente le moment où l'événement-A se termine et beginB représente le moment où l'événement B commence

puis

LocalTime endA = ...;//LocalTime.of(13, 00, 00);
LocalTime beginB = ...;//LocalTime.of(12, 00, 00);
Duration duration = Duration.between(endA, beginB);
System.out.println(duration.getSeconds());

si getSeconds() est négatif, alors ils se chevauchent

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X