【问题标题】:Easiest way to get overlapping times获得重叠时间的最简单方法
【发布时间】:2017-05-19 09:49:49
【问题描述】:

我正面临一个验证,这让我的脑袋冒了很多烟。 我有一个对象,我现在称之为 Downtime,它看起来像这样:

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;
  }
}

当我通过 CRUD 实现创建新的停机时间时,我已经验证了开始时间实际上早于结束时间等等。
现在我必须添加验证,即当我创建新的停机时间时,它不会干扰已经创建的停机时间。这意味着新停机时间的开始日期不存在并且不是另一个停机时间。 (在已经创建的停机时间的开始和结束之间)。

所以我现在这样做的方式是,因为在本地化方面我不擅长面向日期/时间的事情,所以我现在的做法是这样的:

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;
}

代码是在这里写出来的,所以可能有语法错误,但我想你可以明白我想要什么。

现在是我的问题。
上面的代码似乎是一种开销,我想知道如何用更少的代码更快地验证它。

编辑: 让我提供一个清晰的例子

我有一个这样的停机时间列表:

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
}
]

然后我想创建新的停机时间:

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

在此示例中,新的停机时间无效有效,因为它实际上处于另一个已创建停机时间的时间段内。

希望它能让事情更清楚。

编辑 2: 虽然标记的副本包含原因并提供了解决方案,但我也想感谢 Hugo,他在考虑我的标准时提供了很好的答案。

这是我准备的另一个解决方案,它提供了许多更详细的异常和信息处理

/*
 * 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() );

请记住,我的“解决方案”是众多解决方案之一,而且在性能方面也非常密集,因为流是繁重的操作。因此,如果您不需要确切知道有多少碰撞以及它们碰撞的原因,请考虑 Hugo 的回答。

【问题讨论】:

  • 您可以使用period 进行检查。可能会容易得多
  • 您可以对列表/集合进行排序(使用开始时间或结束时间,在这两种情况下您将以相同的顺序结束),然后跳转到 newDowntime 的位置以及它的有效性与 prev。 & 下一项。
  • Period 对此没有好处(它还有其他好的用途)。 @XtremeBaumer
  • @XtremeBaumer,恐怕你误会了。 Period 是两个日期之间的时间量,例如“1 年 7 个月 3 天”。除非误解,否则问题中的停机时间是同一天两个时钟时间之间的时间,例如“从 2017 年 5 月 17 日凌晨 2:15 到 2017 年 5 月 17 日凌晨 3:50”,所以Period 总是等于 0 天。它们是两种截然不同的东西。

标签: java validation date


【解决方案1】:

考虑到:

表示新停机时间的开始日期不存在且不在另一个停机时间。 (在已创建的停机时间的开始和结束之间)。

在这种情况下,您需要将新 DowntimestartDate (downtimeFrom) 与所有现有的 Downtime 进行比较:

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;
}

注意: 在此代码中,如果新的 startDate 等于现有Downtime 的结尾,则Downtime有效。如果您不想这样,可以将第二个if 更改为:

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

【讨论】:

    【解决方案2】:

    查看this question

    或者记住这一点:两个时期会重叠当且仅当

    (StartA = StartB)

    因此,您必须将新的Downtime 与您之前拥有的所有Downtime 进行核对

    【讨论】:

    • 这就是我想要的。所以我的优势是过滤那些我必须只是具有相同日期(没有时间)的那些,然后检查时间。我会考虑你链接的问题。提前谢谢你
    【解决方案3】:

    假设你必须 LocalTime 个对象,

    endA 表示事件-A 何时结束并且 beginB 表示事件-B 的开始时间

    然后

    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());
    

    如果 getSeconds() 为负数,则它们重叠

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-22
      • 2019-04-21
      • 2018-05-06
      相关资源
      最近更新 更多