Java Duration
什么是Duration?
Duration(持续时间)是Java 8引入的时间API的一部分,用于表示时间量或时间段。它主要用于测量两个时间点之间的时间差,精确到纳秒级别。与传统的毫秒计数相比,Duration
提供了更加丰富的API和更精确的时间表示。
Duration
类位于java.time
包中,它是不可变的,线程安全的,可以方便地进行各种时间段的计算和比较操作。
创建Duration对象
基本创建方法
创建Duration对象有多种方法:
java
// 使用of方法创建特定时长的Duration
Duration twoHours = Duration.ofHours(2);
Duration tenMinutes = Duration.ofMinutes(10);
Duration thirtySeconds = Duration.ofSeconds(30);
Duration millis = Duration.ofMillis(1000); // 1秒
Duration nanos = Duration.ofNanos(1_000_000); // 1毫秒
// 使用of方法创建混合时长
Duration duration = Duration.ofSeconds(60, 1_000_000); // 60秒 + 1百万纳秒(1毫秒)
// 输出:PT2H, PT10M, PT30S, PT1S, PT0.001S, PT1M0.001S
System.out.println(twoHours + ", " + tenMinutes + ", " + thirtySeconds + ", " +
millis + ", " + nanos + ", " + duration);
从其他时间对象创建
java
// 从两个时间点创建
Instant start = Instant.now();
// ... 一些操作
Instant end = Instant.now();
Duration elapsed = Duration.between(start, end);
// 从LocalTime创建
LocalTime morning = LocalTime.of(8, 0);
LocalTime evening = LocalTime.of(20, 0);
Duration dayShift = Duration.between(morning, evening);
System.out.println("操作耗时: " + elapsed);
System.out.println("工作时长: " + dayShift); // PT12H (12小时)
备注
Duration
只能表示秒和纳秒级别的时间差,如果需要表示基于日期的时间段(如天、月、年),应该使用Period
类。
Duration的核心方法
获取时间值
java
Duration duration = Duration.ofHours(2).plusMinutes(30).plusSeconds(45);
long seconds = duration.getSeconds(); // 获取总秒数
int nano = duration.getNano(); // 获取纳秒部分
long millis = duration.toMillis(); // 转换为毫秒
long minutes = duration.toMinutes(); // 转换为分钟
long hours = duration.toHours(); // 转换为小时
long days = duration.toDays(); // 转换为天
System.out.println("秒数: " + seconds); // 9045
System.out.println("纳秒部分: " + nano); // 0
System.out.println("毫秒: " + millis); // 9045000
System.out.println("分钟: " + minutes); // 150
System.out.println("小时: " + hours); // 2
System.out.println("天: " + days); // 0
修改Duration
由于Duration
是不可变的,所有修改操作都会返回一个新的Duration
对象:
java
Duration duration = Duration.ofMinutes(30); // 30分钟
// 加法操作
Duration plusDuration = duration.plus(Duration.ofMinutes(10)); // 40分钟
Duration plusHours = duration.plusHours(2); // 2小时30分钟
Duration plusMinutes = duration.plusMinutes(15); // 45分钟
Duration plusSeconds = duration.plusSeconds(60); // 31分钟
Duration plusMillis = duration.plusMillis(1000); // 30分钟1秒
Duration plusNanos = duration.plusNanos(1_000_000); // 30分钟0.001秒
// 减法操作
Duration minusDuration = duration.minus(Duration.ofMinutes(10)); // 20分钟
Duration minusHours = duration.minusHours(1); // -30分钟
Duration minusMinutes = duration.minusMinutes(15); // 15分钟
// 乘除操作
Duration multiplied = duration.multipliedBy(2); // 1小时
Duration divided = duration.dividedBy(2); // 15分钟
// 取绝对值
Duration negative = Duration.ofHours(-5);
Duration absolute = negative.abs(); // 5小时
System.out.println("原始Duration: " + duration);
System.out.println("加10分钟: " + plusDuration);
System.out.println("乘以2: " + multiplied);
System.out.println("除以2: " + divided);
System.out.println("负值的绝对值: " + absolute);
比较Duration
java
Duration duration1 = Duration.ofMinutes(30);
Duration duration2 = Duration.ofSeconds(1800); // 也是30分钟
// 比较
boolean isEqual = duration1.equals(duration2); // true
int comparison = duration1.compareTo(duration2); // 0表示相等
boolean isLonger = duration1.compareTo(Duration.ofMinutes(20)) > 0; // true
boolean isShorter = duration1.compareTo(Duration.ofHours(1)) < 0; // true
// 检查是否为负或零
Duration negative = Duration.ofHours(-1);
boolean isNegative = negative.isNegative(); // true
boolean isZero = duration1.isZero(); // false
System.out.println("是否相等: " + isEqual);
System.out.println("比较结果: " + comparison);
System.out.println("是否为负: " + isNegative);
Duration的格式化和解析
Duration
可以使用ISO-8601格式进行字符串表示和解析:
java
Duration duration = Duration.ofHours(3).plusMinutes(45).plusSeconds(30);
// 格式化为字符串
String durationString = duration.toString(); // PT3H45M30S
// 从字符串解析
Duration parsedDuration = Duration.parse("PT3H45M30S");
Duration anotherDuration = Duration.parse("P2DT3H4M"); // 2天3小时4分钟
System.out.println("格式化后: " + durationString);
System.out.println("解析后: " + parsedDuration);
System.out.println("是否相等: " + duration.equals(parsedDuration)); // true
提示
Duration的字符串表示使用ISO-8601标准:
- 以
P
开头表示一个周期 T
表示时间部分的开始- 后跟时间值和单位(H小时、M分钟、S秒)
实际应用场景
1. 计算操作执行时间
java
public void measureExecutionTime() {
Instant start = Instant.now();
// 进行某些操作,例如排序大型数组
int[] array = new int[1000000];
for (int i = 0; i < array.length; i++) {
array[i] = (int)(Math.random() * 1000000);
}
Arrays.sort(array);
Instant end = Instant.now();
Duration timeElapsed = Duration.between(start, end);
System.out.println("排序耗时: " + timeElapsed.toMillis() + " 毫秒");
}
2. 超时设置
java
public void performTaskWithTimeout() {
Duration timeout = Duration.ofSeconds(5);
Instant start = Instant.now();
boolean completed = false;
try {
// 尝试执行任务
completed = executeTask();
// 检查是否超时
Duration elapsed = Duration.between(start, Instant.now());
if (elapsed.compareTo(timeout) > 0) {
System.out.println("任务执行超时!用时: " + elapsed);
} else {
System.out.println("任务成功完成,用时: " + elapsed);
}
} catch (Exception e) {
System.out.println("任务执行出错: " + e.getMessage());
}
}
private boolean executeTask() {
// 模拟一个可能耗时的任务
try {
Thread.sleep((long)(Math.random() * 6000)); // 0-6秒随机时间
} catch (InterruptedException e) {
return false;
}
return true;
}
3. 定时任务调度
java
public void scheduleWithDuration() {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Duration initialDelay = Duration.ofSeconds(5);
Duration period = Duration.ofMinutes(1);
System.out.println("开始定时任务,初始延迟: " + initialDelay + ", 周期: " + period);
scheduler.scheduleAtFixedRate(
() -> System.out.println("执行定时任务,时间: " + LocalTime.now()),
initialDelay.toMillis(),
period.toMillis(),
TimeUnit.MILLISECONDS
);
// 让示例运行一段时间
try {
Thread.sleep(Duration.ofMinutes(5).toMillis());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
scheduler.shutdown();
}
}
4. 计算过期时间
java
public void calculateExpirationTime() {
// 假设凭证的有效期为30分钟
Duration validity = Duration.ofMinutes(30);
// 创建凭证
Instant creationTime = Instant.now();
Instant expirationTime = creationTime.plus(validity);
System.out.println("凭证创建时间: " + creationTime);
System.out.println("凭证过期时间: " + expirationTime);
// 检查凭证是否过期
Instant currentTime = Instant.now();
boolean isExpired = currentTime.isAfter(expirationTime);
if (isExpired) {
System.out.println("凭证已过期");
} else {
Duration remaining = Duration.between(currentTime, expirationTime);
System.out.println("凭证还有效,剩余时间: " +
remaining.toMinutes() + "分钟 " +
remaining.toSecondsPart() + "秒");
}
}
与其他时间相关类的结合使用
java
// 与LocalDateTime结合使用
LocalDateTime now = LocalDateTime.now();
LocalDateTime later = now.plus(Duration.ofHours(3));
Duration difference = Duration.between(now, later);
// 与Instant结合使用
Instant instantNow = Instant.now();
Instant instantLater = instantNow.plus(Duration.ofDays(1));
Duration dayDifference = Duration.between(instantNow, instantLater);
// 转换为毫秒并用于java.util.Timer
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("任务执行: " + LocalTime.now());
}
}, Duration.ofSeconds(5).toMillis());
System.out.println("3小时后的时间: " + later);
System.out.println("时间差: " + difference);
System.out.println("一天的持续时间: " + dayDifference);
警告
不是所有的时间类都可以使用Duration
进行计算。例如,LocalDate
、YearMonth
等基于日期的类不适用于Duration
计算,应该使用Period
代替。
Duration的局限性
尽管Duration
非常有用,但也有一些局限性:
Duration
只能表示基于时间的数量,无法处理基于日期的时间段(例如1个月),这些情况应使用Period
Duration
主要用于精确的时间测量,不考虑夏令时等时区变化Duration
不支持直接格式化为用户友好的格式,如"2小时30分钟"
java
// 这对于Duration是不可能的:
LocalDate date1 = LocalDate.of(2023, 1, 1);
LocalDate date2 = LocalDate.of(2023, 3, 1);
// Duration dateDiff = Duration.between(date1, date2); // 会抛出异常
// 应该使用Period:
Period period = Period.between(date1, date2);
System.out.println("两个日期之间的差: " + period); // P2M (2个月)
总结
Java的Duration
类提供了一种精确、灵活且线程安全的方式来表示和操作时间段。它是Java 8时间API的核心组成部分,特别适合处理需要高精度时间测量的场景。
主要优点包括:
- 提供纳秒级精度
- 丰富的API支持各种时间计算
- 不可变设计确保线程安全
- 与Java 8其他时间类无缝集成
使用Duration
可以大大简化与时间相关的各种操作,从简单的时间差计算到复杂的定时任务和超时处理。
练习
- 创建一个计时器类,使用
Duration
来测量和显示代码块执行所需的时间。 - 实现一个方法,接受一个
Duration
参数并返回格式化的字符串,如"2小时15分钟30秒"。 - 编写一个程序,比较使用
Duration
和传统毫秒计数方法测量时间的差异。 - 创建一个定时任务调度器,允许用户使用
Duration
对象指定任务的初始延迟和周期。
进一步学习资源
掌握Duration
类是成为Java高效开发者的重要一步,它帮助我们以更清晰、更精确的方式处理时间相关的逻辑。