Java 基础教程

Java 面向对象

Java 高级教程

Java 笔记

java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Years 错误原因及解决方法

Java 日期和时间 Java 日期和时间


基于年粒度,使用 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();