1

我试图在当前时间上增加几年。我的代码如下所示:

// ten yeas ago
int backYears = 10;
Instant instant = ChronoUnit.YEARS.addTo(Instant.now(), -backYears);

但我有一个例外:

java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Years
at java.time.Instant.plus(Instant.java:862)

当我打开该方法时,Instant.plus我看到以下内容:

@Override
public Instant plus(long amountToAdd, TemporalUnit unit) {
    if (unit instanceof ChronoUnit) {
        switch ((ChronoUnit) unit) {
            case NANOS: return plusNanos(amountToAdd);
            case MICROS: return plus(amountToAdd / 1000_000, (amountToAdd % 1000_000) * 1000);
            case MILLIS: return plusMillis(amountToAdd);
            case SECONDS: return plusSeconds(amountToAdd);
            case MINUTES: return plusSeconds(Math.multiplyExact(amountToAdd, SECONDS_PER_MINUTE));
            case HOURS: return plusSeconds(Math.multiplyExact(amountToAdd, SECONDS_PER_HOUR));
            case HALF_DAYS: return plusSeconds(Math.multiplyExact(amountToAdd, SECONDS_PER_DAY / 2));
            case DAYS: return plusSeconds(Math.multiplyExact(amountToAdd, SECONDS_PER_DAY));
        }
        throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
    }
    return unit.addTo(this, amountToAdd);
}

如您所见MONTHSYEARS不受支持。但为什么?有了一个旧的java.util.Calendar,我可以很容易地做到这一点:

    Calendar c = Calendar.getInstance();
    c.setTime(date);
    c.add(Calendar.YEAR, amount);
    return c.getTime(); 

我猜唯一的一个原因是,由于闰日 2 月 29 日,我们不知道一个月和一年中有多少天。但老实说,我们也有闰秒。因此,我认为这是一个错误,ChronoUnit应该支持所有 s。唯一的一个问题是:我们是否需要考虑闰秒和闰日。至于我的需要,可以假设那个月有 30 天和 365 年。我不需要做类似的事情,Calendar.roll()但这也可以满足我。

4

1 回答 1

2

让我们尝试一下。我ZonedDateTime在不同的时区减去 10 年。

    OffsetDateTime origin = OffsetDateTime.of(2018, 3, 1, 0, 0, 0, 0, ZoneOffset.UTC);
    Instant originInstant = origin.toInstant();
    Instant tenYearsBackKyiv = origin.atZoneSameInstant(ZoneId.of("Europe/Kiev"))
            .minusYears(10)
            .toInstant();
    long hoursSubtractedKyiv = ChronoUnit.HOURS.between(tenYearsBackKyiv, originInstant);
    System.out.println("Hours subtracted in Київ: " + hoursSubtractedKyiv);
    Instant tenYearsBackSaoPaulo = origin.atZoneSameInstant(ZoneId.of("America/Sao_Paulo"))
            .minusYears(10)
            .toInstant();
    long hoursSubtractedSaoPaulo = ChronoUnit.HOURS.between(tenYearsBackSaoPaulo, originInstant);
    System.out.println("Hours subtracted in São Paulo: " + hoursSubtractedSaoPaulo);

输出是:

Hours subtracted in Київ: 87648
Hours subtracted in São Paulo: 87672

如您所见,与 Київ(基辅,基辅)相比,圣保罗多出 24 小时(多出 1 天)。您可能已经知道这是因为我们在闰年从 3 月 1 日到 2 月 29 日经过了 3 次,而在 Київ 只有两次。

旧的和现在过时Calendar的类总是有一个时区,所以知道在哪个时区减去年份(另一件事是,即使在不清楚你想要哪个结果的情况下,它也很乐意给你一个结果)。现代班ZonedDateTimeOffsetDateTimeLocalDateTime可以这样做。所以使用它们。Instant概念上没有时区,因此拒绝执行依赖于时区的操作(我知道它是使用 UTC 实现的,但我们应该将其视为不相关的实现细节,而不是作为接口规范的一部分班上)。

旧的和现代的课程都没有考虑闰秒,你是对的,只有因此才能Instant加减天、小时和分钟。

于 2017-11-11T14:08:40.783 回答