Java 中的 Period 和 Duration

98 阅读5分钟

在本文中让我们来看看在 Java 8 中引入的 2 个新的类:

Period

Duration

.

上面 2 个类可以被用来替换在 determine 和 time 中大量使用用来计算 2 个时间不同的 API。

针对上面 2 个类最主要的不同就是

Period

被用来计算日期的不同,

Duration

则是被用来计算时间的不同。

Period 类

Period

使用的单位是 年,月,日来表达 2 个日期之间的不同。

我们可以通过 2 个时期之间不同的

between()

方法来获得

Period

对象。

LocalDate startDate = LocalDate.of(2015, 2, 20);
LocalDate endDate = LocalDate.of(2017, 1, 15);

Period period = Period.between(startDate, endDate);

当我们获得 Period 对象后,我们可以用 Period 对象中的 *getYears(), getMonths(), getDays() 方法来获得具体的值。

        logger.info(String.format("Years:%d months:%d days:%d", period.getYears(), period.getMonths(), period.getDays()));

2025-01-17_06-47-08

isNegative()

也是 Period 中的一个方法,可以通过这个方法来判断 2 个比较时间的大小关系。

当这个方法返回 True 的时候,表明结束时间要大于起始时间。

assertFalse(period.isNegative());

相反的

isNegative()

如果返回的为 false,那么表明 startDate* 要早入

endDate

的值。

另外一个可以创建

Period

对象的方法就是使用数字,Period 中提供了一个 of 方法,我们可以用这个方法来构造一个 Period 对象:

Period fromUnits = Period.of(3, 10, 10);
Period fromDays = Period.ofDays(50);
Period fromMonths = Period.ofMonths(5);
Period fromYears = Period.ofYears(10);
Period fromWeeks = Period.ofWeeks(40);

assertEquals(280, fromWeeks.getDays());

如果 of 方法中只有一个输入参数的话,那么默认调用的是

ofDays()

方法,等于用输入的数字初始化日这个单位。,

基于上面的设计,

ofWeeks()

方法使用的是乘以 7 的方式,因为不管一年有多少天,但一周 7 天这个肯定是不会变的。

通过看 JDK 的源代码:

    public static Period ofWeeks(int weeks) {
        return create(0, 0, Math.multiplyExact(weeks, 7));
    }

2025-01-17_06-53-42

上面的构造方法就能说明这个问题了。

同时,还可以使用字符串来创建

Period

对象,这个字符串使用的序列为 “PnYnMnD”:

Period fromCharYears = Period.parse("P2Y");
assertEquals(2, fromCharYears.getYears());

Period fromCharUnits = Period.parse("P2Y3M5D");
assertEquals(5, fromCharUnits.getDays());

当 period 对象创建成功后,可以使用

plusX()

minusX()

方法来对创建的 period 对象中的值进行调整:

assertEquals(56, period.plusDays(50).getDays());
assertEquals(9, period.minusMonths(2).getMonths());

Duration 类

和 Period 相对,Duration 类是对时间进行操作的。

具体操作的单位为秒(seconds )和纳秒(nanoseconds )。

因可以直接对纳秒进行操作,所以 Duration 能比较精确的对时间进行计算。

另外,要获得 Duration 对象,我们需要从 instants 来进行比较,使用 between() 方法来比较 instants 。

Instant start = Instant.parse("2017-10-03T10:15:30.00Z");
Instant end = Instant.parse("2017-10-03T10:16:30.00Z");
        
Duration duration = Duration.between(start, end);

上面的方法能够构造一个 Duration 对象,然后从 Duration 对象中使用 getSeconds() 或者getNanoseconds() 方法来获得具体的单位值:

assertEquals(60, duration.getSeconds());

可选的,因为是对时间进行计算机,那么你还可以从 LocalDateTime 实例中来比较构造一个 Duration 对象:

LocalDateTime start = LocalDateTime.parse("2020-01-01T08:00:00");
LocalDateTime end = LocalDateTime.parse("2023-01-01T12:00:00");

Duration.between(start, end).getSeconds();

基于起始时间的不同,Duration 可能会有下面的 3 中情况:

  • Negative: start < end
  • Zero: start = end
  • Positive: start > end

同时,标准的时间 Time API 也提供了转发方法来检查 Duration 是否为 0 或者负数:

LocalDateTime start = LocalDateTime.parse("2020-01-01T08:00:00");
LocalDateTime end = LocalDateTime.parse("2020-01-01T12:00:00");
assertFalse(Duration.between(start, end).isNegative());
assertTrue(Duration.between(end, start).isNegative());

LocalDateTime theTime = LocalDateTime.parse("2023-09-09T08:00:00");
assertTrue(Duration.between(theTime,theTime).isZero());
assertFalse(Duration.between(theTime,theTime).isNegative());


// Java version 18+ required
LocalDateTime start = LocalDateTime.parse("2020-01-01T08:00:00");
LocalDateTime end = LocalDateTime.parse("2020-01-01T12:00:00");
assertTrue(Duration.between(start, end).isPositive());
assertFalse(Duration.between(end, start).isPositive());

当然,我们也可以使用单位来构造一个 Duration 对象,这个和 Period 的构造方法基本相同。

可以使用的方法有:

ofDays(), ofHours(), ofMillis(), ofMinutes(), ofNanos(), ofSeconds()

:

Duration fromDays = Duration.ofDays(1);
assertEquals(86400, fromDays.getSeconds());
       
Duration fromMinutes = Duration.ofMinutes(60);

我们还可以使用字符串的方法来构造

Duration

,表达方式为:

PnDTnHnMn.nS

Duration fromChar1 = Duration.parse("P1DT1H10M10.5S");
Duration fromChar2 = Duration.parse("PT10M");

duration 对象也可以实现其他单位的转换,使用的方法为:

toDays(), toHours(), toMillis(), toMinutes()

assertEquals(1, fromMinutes.toHours());

针对时间的增加和减少,我们可以使用

plusX()

方法

minusX()

,

X

根据具体的单位不同而使用不同的字符来表达,具体的可以参考 JDK API 的手册。

下面我们只简单的列了几个可以使用的方法:

assertEquals(120, duration.plusSeconds(60).getSeconds());     
assertEquals(30, duration.minusSeconds(30).getSeconds());

为了避免书写上面的问题,我们还可以在

plus()

minus()

方法中使用

ChronoUnit

来制定添加或者减少的单位:

assertEquals(120, duration.plus(60, ChronoUnit.SECONDS).getSeconds());     
assertEquals(30, duration.minus(30, ChronoUnit.SECONDS).getSeconds());

结论

让我为你总结一下这两个类的主要区别和用法:

Period 和 Duration 的主要区别

  1. Period
    • 使用基于日期的值(date-based values)
    • 主要用于处理年、月、日的时间段
    • 适用于较长时间段的计算
  2. Duration
    • 使用基于时间的值(time-based values)
    • 主要用于处理秒和纳秒级别的时间
    • 适用于更短、更精确的时间间隔

Period 的主要用法

  1. 创建 Period 对象:

    // 通过两个日期之间的差值创建 LocalDate startDate = LocalDate.of(2015, 2, 20); LocalDate endDate = LocalDate.of(2017, 1, 15); Period period = Period.between(startDate, endDate);

    // 直接创建特定时间段 Period fromUnits = Period.of(3, 10, 10); // 年、月、日 Period fromDays = Period.ofDays(50); Period fromMonths = Period.ofMonths(5); Period fromYears = Period.ofYears(10); Period fromWeeks = Period.ofWeeks(40);

  2. 解析字符串创建 Period:

    Period fromCharYears = Period.parse("P2Y"); // 2年 Period fromCharUnits = Period.parse("P2Y3M5D"); // 2年3月5日

Duration 的主要用法

  1. 创建 Duration 对象:

    // 通过两个时间点之间的差值创建 Instant start = Instant.parse("2017-10-03T10:15:30.00Z"); Instant end = Instant.parse("2017-10-03T10:16:30.00Z"); Duration duration = Duration.between(start, end);

    // 直接创建特定时长 Duration fromDays = Duration.ofDays(1); Duration fromMinutes = Duration.ofMinutes(60);

  2. Duration 的其他操作:

    // 检查 Duration 的性质 duration.isNegative(); duration.isZero(); duration.isPositive(); // Java 18+

    // 转换到其他时间单位 duration.toDays(); duration.toHours(); duration.toMillis(); duration.toMinutes();

    // 增加或减少时长 duration.plusSeconds(60); duration.minusSeconds(30);

使用建议

  1. 如果你需要处理年、月、日级别的时间段,使用 Period
  2. 如果你需要更精确的时间计算(包括小时、分钟、秒、纳秒),使用 Duration
  3. Period 可能会损失精度,而 Duration 则保持高精度

这两个类都是在 Java 8 中引入的,属于新的日期时间 API 的一部分,它们提供了更清晰和更易用的时间间隔处理方式。

www.isharkfly.com/t/java-peri…