有没有办法表达 TemporalAmount 的 "sign"?

Is there a way to express the "sign" of a TemporalAmount?

基于这个 answer,我决定实现我自己的 MutableClock 用于单元测试目的,但工作方式略有不同:

class MutableClock extends Clock {
  private final List<Instant> instants = new ArrayList<>();

  public MutableClock(Instant now, TemporalAmount... amounts) {
    Objects.requireNonNull(now, "now must not be null");
    Objects.requireNonNull(amounts, "amounts must not be null");
    instants.add(now);
    for (TemporalAmount amount : amounts) {
        Instant latest = instants.get(instants.size() - 1);
        instants.add(latest.plus(amount));
    }
  }

  @Override
  public Instant instant() {
    Instant latest = instants.get(0);
    if (instants.size() > 1) {
        instants.remove(0);
    }
    return latest;
  }

  ... 

但后来我注意到我在这里这样做:

instants.add(latest.plus(amount));

所以,基本上,我只能打勾"forward"。当然,这在大多数情况下都是有意义的,但由于所有这些都是为了单元测试,我可以想象我想要使用这样一个 MutableClock 实例并拥有它 return 并不总是这样的瞬间"increasing"。

但是看TemporalAmount界面时:negative的时间量是没有办法表达的?!换句话说:似乎 TemporalAmount 的实例不是 "signed"。

那么,怎么办呢?

这个问题可以很直接地解决:只需查看该接口的具体实现,即 Duration. That class actually offers negated() 好吧,否定 Duration 实例。

因此,当传递负持续时间时,上述实现已经有效:

@Test
public void testNegativeAdjustments() {
    Instant now = Instant.now();
    Duration amount = Duration.ofSeconds(5);
    TemporalAmount negativeAmount = amount.negated();
    MutableClock underTest = new MutableClock(now, negativeAmount);
    assertThat(underTest.instant(), is(now));
    assertThat(underTest.instant(), is(amount.subtractFrom(now)));
}

TemporalAmount 由 java.time 中的两个 class 实现:DurationPeriod。当然,用户也可以编写他或她自己的接口实现。我没有检查,但我假设 ThreeTen Extra 项目的 PeriodDuration class 也实现了该接口。您可能认为不能将 Period 添加到 Instant,因为 Instant 不知道日期、月份和年份,而这正是 Period 的组成部分。一般来说,你确实不能,但在某些情况下你可以。添加 Period.ZEROPeriod.ofWeeks(3) 会很好(后者将一周定义为 168 小时,我们知道夏令时开始和结束时,这不是真的,但这是可能的)。简而言之:我们不能安全地假设 TemporalAmountDuration.

如果你想在接口上编程,一个相当简单的技巧就是在添加金额时检查时钟是否真的倒退了:

        Instant latest = instants.get(instants.size() - 1);
        Instant newInstant = latest.plus(amount);
        if (newInstant.isBefore(latest)) {
            // The amount was negative; do whatever handling of the situation you need
        } else {
            instants.add(newInstant);
        }

当然,如果你想让你的时钟倒退,则不需要对这种情况进行特殊处理(你可以省略 if-else 结构)。正如您在自己的回答中指出的那样,创建负数没有问题,例如 Duration.ofSeconds(-5)Period.ofWeeks(-3).

为什么接口不提供测试负数的and/or取反方法?

虽然 Duration 总是明确地为负数、零或正数,但这不适用于 PeriodPeriod.ofMonths(1).minusDays(30) 可能为负数、零或正数,具体取决于月份的选择。奇怪的是Period有一个isNegative方法,但它只是测试三个单位(年,月,日)中的任何一个是否为负,所以语义不是你需要的。所以 Period.ofMonths(1).minusDays(3).isNegative() returns true.