在本文中让我们来看看在 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()));
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));
}
上面的构造方法就能说明这个问题了。
同时,还可以使用字符串来创建
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 的主要区别
- Period
- 使用基于日期的值(date-based values)
- 主要用于处理年、月、日的时间段
- 适用于较长时间段的计算
- Duration
- 使用基于时间的值(time-based values)
- 主要用于处理秒和纳秒级别的时间
- 适用于更短、更精确的时间间隔
Period 的主要用法
-
创建 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);
-
解析字符串创建 Period:
Period fromCharYears = Period.parse("P2Y"); // 2年 Period fromCharUnits = Period.parse("P2Y3M5D"); // 2年3月5日
Duration 的主要用法
-
创建 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);
-
Duration 的其他操作:
// 检查 Duration 的性质 duration.isNegative(); duration.isZero(); duration.isPositive(); // Java 18+
// 转换到其他时间单位 duration.toDays(); duration.toHours(); duration.toMillis(); duration.toMinutes();
// 增加或减少时长 duration.plusSeconds(60); duration.minusSeconds(30);
使用建议
- 如果你需要处理年、月、日级别的时间段,使用 Period
- 如果你需要更精确的时间计算(包括小时、分钟、秒、纳秒),使用 Duration
- Period 可能会损失精度,而 Duration 则保持高精度
这两个类都是在 Java 8 中引入的,属于新的日期时间 API 的一部分,它们提供了更清晰和更易用的时间间隔处理方式。