基于年粒度,使用 java 8 的 time api 进行时间跨度计算时,出现报错 java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Years
。
错误示例及原因
查看源码才知道,Instant 的 minus 及 plus 等方法操作居然不支持 ChronoUnit.YEARS 即年粒度的操作。
获取当前时间戳的一年前时间,如下:
long time = Instant.now().minus(1, ChronoUnit.YEARS).toEpochMilli();
出现如下报错:
java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Years at java.time.Instant.plus(Instant.java:862) at java.time.Instant.minus(Instant.java:979) ...... ......
通过错误异常栈发现,minus 操作其实本质上调用了 plus 操作,查看其源码惊奇地发现确实不支持年份操作:
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);
}
上述源码发现 case 里面确实没有 YEARS 的分支,不知源码设计者出于什么考虑。
解决办法
主要有两种解决方式,一种是如果年的计算是粗略的计算,如无需区分闰年等信息,就可以按照一年 365 天计算,进行 DAYS 粒度的操作即可,继续使用 java 8 的 time api;另一种是用 Calendar 类相关的 api。
long time = Instant.now().minus(365, ChronoUnit.DAYS).toEpochMilli();
使用 Calendar api,具体如下:
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.YEAR, -1);
long time = calendar.getTimeInMillis();