Java 日期和时间 (JSR 310):时间范围是否包含值而不迭代所有值

Java Date and Time (JSR 310): does a temporal range contain value without iterating all of them

我有一个自定义范围 (~ Collection),它有 2 个 Temporal 边界(fromto)并且可以枚举这两个边界之间的所有值通过给定的 TemporalUnit incrementUnitType.

增加时间
private final Temporal_ from;
private final Temporal_ to;
private final TemporalUnit incrementUnitType; // For example Month, Day,  Minute, ...

在其中,我需要实现一个 contains 方法,以检查遍历该范围是否包含特定值。例如,如果它包含 8 March。这是我想编写该方法的方式:

public boolean contains(Temporal_ value) {
    ...
    if (from.until(value, incrementUnitType) < 0
            || value.until(to, incrementUnitType) <= 0) {
        return false; // Out of bounds
    }
    // This doesn't work because 1-MAR + 1 month doesn't include 8-MAR
    return true;
}

这里有一些迭代:

在最后一种情况下,上面的代码对于 8-MAR 不正确 return true。这是我需要如何编写该代码才能工作,方法是执行 for 循环并检查每个可能的值:

public boolean contains(Temporal_ value) {
    ...
    if (from.until(value, incrementUnitType) < 0
            || value.until(to, incrementUnitType) <= 0) {
        return false; // Out of bounds
    }
    // This works but it kills scalability
    for (long i = 0; i < getSize(); i++) {
        Temporal_ temporal = get(i);
        if (value.equals(temporal)) {
            return true;
        }
    }
    return false;
}

这是一个可扩展性问题。有什么办法可以避免这种情况吗?

最有效的解决方案是为每个增量单元单独逻辑。

  • 使用 DAYS,您可以只检查日期是否在开始和结束之间。
  • 使用 WEEKS,您可以对两者都调用 toEpochDays(),减去它们并检查它是否除以 7
  • 使用 MONTHS,您可以检查日期是否相同

如果可以避免,我会警告不要使用 Temporal 作为输入类型,因为它会使处理变得更加困难。一种选择,如果您必须使用它来调用输入上的 LocalDate.from(temporal) 以允许逻辑在 LocalDate.

上内部工作

您可以使用方法 Temporal.until 来获得结果。因为它 returns "the number of complete units between the two temporals" (doc),您可以将此数字添加到 from 并查看它是否等于该值,但这不会立即起作用。

long wholeAmount = from.until(value, incrementUnitType)
return value.equals(from.plus(wholeAmount, incrementUnitType));

如您所见,问题在于某些时间单位的持续时间并不总是相同(例如,一个月可能有 28、29、30 或 31 天)。 untilplus 可能不一致(即 temporal.until(temporal.plus(1,ChronoUnit.MONTHS),ChronoUnit.MONTHS) 可能为 0,而我们添加了一个月)。

引用 doc :

In some cases, changing a field is not fully defined. For example, if the target object is a date representing the 31st January, then adding one month would be unclear. In cases like this, the field is responsible for resolving the result. Typically it will choose the previous valid date, which would be the last valid day of February in this example.

这意味着我们还应该检查 wholeAmount + 1,以防四舍五入。

// ... out of bound check
// ...
long wholeAmount = from.until(value, incrementUnitType)
return value.equals(from.plus(wholeAmount, incrementUnitType)) || value.equals(from.plus(wholeAmount + 1, incrementUnitType));

处理 Temporal 似乎很棘手,所以我不能确定它是否适用于每个单位和每个时间(一定要检查 incrementUnitType 是否与您的时间兼容)。可能有几种边缘情况需要测试。