当前位置: 首页 > news >正文

Duration详解

Java Duration 类:时间计算从此变简单

前言

你有没有遇到过这样的需求:

  • 计算两个时间点之间相差多少秒?
  • 判断一个任务执行了多长时间?
  • 给当前时间加上 2 小时 30 分钟?
  • 判断某个操作是否超时(比如超过 5 秒)?

以前用 DateCalendar 处理这些问题,代码写起来又臭又长,还容易出错。自从 Java 8 引入了新的时间 API 后,一切都变简单了。

今天我们就来聊聊 Duration 这个类,它专门用来表示时间段(持续时间),比如"3小时"、“120秒”、"2天"等。掌握了它,时间计算再也不是难题。

第一章:Duration 是什么?

1.1 时间点 vs 时间段

首先要区分两个概念:

  • 时间点:某个具体的时刻,比如 “2025年11月13日 15:30:00”

    • Java 中用 InstantLocalDateTime 等表示
  • 时间段:一段持续时间,比如 “3小时”、“120秒”

    • Java 中用 Duration 表示

打个比方:

  • 时间点就像地图上的一个坐标:北京市朝阳区某某街
  • 时间段就像两点之间的距离:从 A 点到 B 点相距 5 公里

1.2 Duration 的定义

Duration 是 Java 8 引入的类,用来表示基于时间的时间段。

关键特点:

  • java.time 包下(不是老的 java.util.Date
  • 不可变类(immutable),线程安全
  • 精度到纳秒(1秒 = 10亿纳秒)
  • 内部存储:秒数 + 纳秒数
// Duration 内部结构(简化版)
class Duration {private final long seconds;  // 秒数private final int nanos;     // 纳秒数(0-999,999,999)
}

1.3 Duration 从哪来?

**JDK 自带!**不需要引入任何第三方库。

import java.time.Duration;  // JDK 8+ 自带

要求:JDK 8 或更高版本。如果你的项目还在用 JDK 7,那就只能用老的 DateCalendar 了(或者考虑升级 JDK 😄)。

第二章:创建 Duration 对象

Duration 是不可变类,不能用 new Duration() 创建,必须通过静态工厂方法。

2.1 基于时间单位创建

最常用的几个方法:

import java.time.Duration;// 创建 5 秒
Duration duration1 = Duration.ofSeconds(5);// 创建 3 分钟
Duration duration2 = Duration.ofMinutes(3);// 创建 2 小时
Duration duration3 = Duration.ofHours(2);// 创建 1 天(24小时)
Duration duration4 = Duration.ofDays(1);// 创建 500 毫秒
Duration duration5 = Duration.ofMillis(500);// 创建 1000 纳秒
Duration duration6 = Duration.ofNanos(1000);

完整列表:

方法说明示例
ofNanos(long)纳秒Duration.ofNanos(1000)
ofMillis(long)毫秒Duration.ofMillis(500)
ofSeconds(long)Duration.ofSeconds(30)
ofSeconds(long, long)秒 + 纳秒Duration.ofSeconds(5, 500_000_000) → 5.5秒
ofMinutes(long)分钟Duration.ofMinutes(10)
ofHours(long)小时Duration.ofHours(2)
ofDays(long)Duration.ofDays(1)

2.2 基于字符串创建(ISO-8601 格式)

Duration 支持 ISO-8601 标准的字符串格式:

// 格式:PT[小时]H[分钟]M[秒]S
Duration d1 = Duration.parse("PT2H");        // 2小时
Duration d2 = Duration.parse("PT15M");       // 15分钟
Duration d3 = Duration.parse("PT30S");       // 30秒
Duration d4 = Duration.parse("PT2H30M");     // 2小时30分钟
Duration d5 = Duration.parse("PT1H15M30S");  // 1小时15分30秒// 负数表示"之前"
Duration d6 = Duration.parse("-PT2H");       // -2小时(2小时前)// 天数
Duration d7 = Duration.parse("P2D");         // 2天
Duration d8 = Duration.parse("P2DT3H");      // 2天3小时

格式说明:

  • P 开头表示 Period(时间段)
  • T 分隔日期和时间部分
  • D = Days(天)
  • H = Hours(小时)
  • M = Minutes(分钟)
  • S = Seconds(秒)

注意PT 后面没有日期部分时必须有,P2D 表示 2 天,PT2H 表示 2 小时。

2.3 计算两个时间点之间的 Duration

这是最实用的创建方式!

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.Duration;// 方式1:使用 Instant(推荐)
Instant start = Instant.now();
// ... 执行一些操作
Thread.sleep(2000);  // 模拟耗时操作
Instant end = Instant.now();Duration duration = Duration.between(start, end);
System.out.println("耗时: " + duration.toMillis() + " 毫秒");// 方式2:使用 LocalDateTime
LocalDateTime dateTime1 = LocalDateTime.of(2025, 11, 13, 10, 0, 0);
LocalDateTime dateTime2 = LocalDateTime.of(2025, 11, 13, 12, 30, 0);Duration duration2 = Duration.between(dateTime1, dateTime2);
System.out.println("相差: " + duration2.toHours() + " 小时");  // 2小时// 方式3:使用 LocalTime(只比较时间部分)
LocalTime time1 = LocalTime.of(10, 30);
LocalTime time2 = LocalTime.of(14, 45);Duration duration3 = Duration.between(time1, time2);
System.out.println("相差: " + duration3.toMinutes() + " 分钟");  // 255分钟

2.4 特殊的 Duration 常量

// 零时长
Duration zero = Duration.ZERO;// 也可以这样创建零时长
Duration zero2 = Duration.ofSeconds(0);

第三章:获取 Duration 的值

创建了 Duration 对象后,怎么获取它的值呢?

3.1 转换为不同单位

Duration duration = Duration.ofHours(2).plusMinutes(30).plusSeconds(45);
// 2小时30分45秒// 转换为不同单位
long days = duration.toDays();           // 0 天(不足1天)
long hours = duration.toHours();         // 2 小时
long minutes = duration.toMinutes();     // 150 分钟
long seconds = duration.toSeconds();     // 9045 秒(2*3600 + 30*60 + 45)
long millis = duration.toMillis();       // 9045000 毫秒
long nanos = duration.toNanos();         // 9045000000000 纳秒System.out.println("总共 " + seconds + " 秒");  // 9045 秒
System.out.println("总共 " + minutes + " 分钟");  // 150 分钟

注意:这些方法返回的是总量,比如 toMinutes() 返回的是总分钟数,不是"小时部分去掉后剩下的分钟"。

3.2 获取组成部分

如果你想要"2小时30分45秒"这样分开的信息:

Duration duration = Duration.ofHours(2).plusMinutes(30).plusSeconds(45);// 获取总秒数
long totalSeconds = duration.getSeconds();  // 9045// 获取纳秒部分(不足1秒的部分)
int nanos = duration.getNano();  // 0(因为没有不足1秒的部分)// 如何分解成"小时:分钟:秒"?需要手动计算
long hours = duration.toHours();
long minutes = duration.toMinutesPart();  // JDK 9+
long seconds = duration.toSecondsPart();  // JDK 9+System.out.println(hours + "小时 " + minutes + "分钟 " + seconds + "秒");
// 输出:2小时 30分钟 45秒

JDK 9+ 新增方法:

方法说明返回值范围
toDaysPart()天数部分任意 long
toHoursPart()小时部分0-23
toMinutesPart()分钟部分0-59
toSecondsPart()秒部分0-59
toMillisPart()毫秒部分0-999
toNanosPart()纳秒部分0-999,999,999

JDK 8 中的替代方案:

Duration duration = Duration.ofHours(2).plusMinutes(30).plusSeconds(45);long totalSeconds = duration.getSeconds();
long hours = totalSeconds / 3600;
long minutes = (totalSeconds % 3600) / 60;
long seconds = totalSeconds % 60;System.out.println(hours + ":" + minutes + ":" + seconds);
// 输出:2:30:45

3.3 判断正负和零

Duration positive = Duration.ofHours(2);
Duration negative = Duration.ofHours(-2);
Duration zero = Duration.ZERO;// 判断是否为零
System.out.println(zero.isZero());      // true
System.out.println(positive.isZero());  // false// 判断是否为负数
System.out.println(negative.isNegative());  // true
System.out.println(positive.isNegative());  // false
System.out.println(zero.isNegative());      // false

第四章:Duration 的计算

Duration 提供了丰富的计算方法,而且都是不可变操作(返回新对象,不修改原对象)。

4.1 加法操作

Duration duration = Duration.ofHours(1);// 加秒
Duration d1 = duration.plusSeconds(30);  // 1小时30秒// 加分钟
Duration d2 = duration.plusMinutes(15);  // 1小时15分钟// 加小时
Duration d3 = duration.plusHours(2);     // 3小时// 加天
Duration d4 = duration.plusDays(1);      // 25小时// 加毫秒
Duration d5 = duration.plusMillis(500);  // 1小时500毫秒// 加纳秒
Duration d6 = duration.plusNanos(1000);  // 1小时1000纳秒// 加另一个 Duration
Duration extra = Duration.ofMinutes(30);
Duration d7 = duration.plus(extra);      // 1小时30分钟

4.2 减法操作

Duration duration = Duration.ofHours(3);// 减秒
Duration d1 = duration.minusSeconds(30);  // 2小时59分30秒// 减分钟
Duration d2 = duration.minusMinutes(15);  // 2小时45分钟// 减小时
Duration d3 = duration.minusHours(1);     // 2小时// 减天
Duration d4 = duration.minusDays(1);      // -21小时(负数!)// 减另一个 Duration
Duration subtract = Duration.ofMinutes(30);
Duration d5 = duration.minus(subtract);   // 2小时30分钟

4.3 乘法和除法

Duration duration = Duration.ofHours(2);// 乘法
Duration doubled = duration.multipliedBy(2);   // 4小时
Duration tripled = duration.multipliedBy(3);   // 6小时// 除法
Duration half = duration.dividedBy(2);         // 1小时
Duration third = duration.dividedBy(3);        // 40分钟

4.4 取反和绝对值

Duration duration = Duration.ofHours(2);// 取反(正变负,负变正)
Duration negated = duration.negated();  // -2小时Duration negative = Duration.ofHours(-3);
Duration positive = negative.negated(); // 3小时// 绝对值
Duration abs1 = Duration.ofHours(-2).abs();  // 2小时
Duration abs2 = Duration.ofHours(2).abs();   // 2小时

4.5 链式调用(推荐)

由于 Duration 是不可变的,所以可以优雅地链式调用:

Duration duration = Duration.ofHours(1).plusMinutes(30).plusSeconds(45).plusMillis(500);
// 1小时30分45.5秒// 计算复杂时间
Duration workTime = Duration.ofHours(8)   // 8小时工作时间.minusMinutes(60)                      // 减去1小时午休.minusMinutes(30);                     // 减去30分钟茶歇
// 实际工作时间:6.5小时System.out.println("实际工作: " + workTime.toHours() + "小时"+ workTime.toMinutesPart() + "分钟");  // JDK 9+

第五章:Duration 的比较

5.1 比较大小

Duration d1 = Duration.ofHours(2);
Duration d2 = Duration.ofMinutes(90);  // 1.5小时
Duration d3 = Duration.ofHours(2);// 使用 compareTo(返回 -1、0、1)
System.out.println(d1.compareTo(d2));  // 1(d1 > d2)
System.out.println(d2.compareTo(d1));  // -1(d2 < d1)
System.out.println(d1.compareTo(d3));  // 0(d1 == d3)// 更直观的方法(JDK 8+)
System.out.println(d1.equals(d3));  // true
System.out.println(d1.equals(d2));  // false

5.2 判断大小关系

虽然没有直接的 isGreaterThan() 方法,但可以用 compareTo

Duration d1 = Duration.ofHours(2);
Duration d2 = Duration.ofHours(1);// 判断 d1 是否大于 d2
boolean isGreater = d1.compareTo(d2) > 0;  // true// 判断 d1 是否小于 d2
boolean isLess = d1.compareTo(d2) < 0;     // false// 判断是否相等
boolean isEqual = d1.compareTo(d2) == 0;   // false

工具方法封装(推荐):

public class DurationUtils {public static boolean isGreaterThan(Duration d1, Duration d2) {return d1.compareTo(d2) > 0;}public static boolean isLessThan(Duration d1, Duration d2) {return d1.compareTo(d2) < 0;}
}// 使用
if (DurationUtils.isGreaterThan(actualTime, expectedTime)) {System.out.println("超时了!");
}

第六章:Duration 格式化

6.1 默认的 toString()

Duration d1 = Duration.ofHours(2).plusMinutes(30).plusSeconds(45);
System.out.println(d1.toString());  // PT2H30M45SDuration d2 = Duration.ofDays(1).plusHours(3);
System.out.println(d2.toString());  // PT27H(不会显示为 P1DT3H)

toString() 返回的是 ISO-8601 格式,不太友好。

6.2 自定义格式化

Duration 本身没有提供格式化方法,需要手动处理:

public class DurationFormatter {/*** 格式化为 "HH:MM:SS"*/public static String formatHMS(Duration duration) {long hours = duration.toHours();long minutes = duration.toMinutesPart();  // JDK 9+long seconds = duration.toSecondsPart();  // JDK 9+return String.format("%02d:%02d:%02d", hours, minutes, seconds);}/*** 格式化为中文 "X小时X分钟X秒"*/public static String formatChinese(Duration duration) {long hours = duration.toHours();long minutes = duration.toMinutesPart();  // JDK 9+long seconds = duration.toSecondsPart();  // JDK 9+if (hours > 0) {return hours + "小时" + minutes + "分钟" + seconds + "秒";} else if (minutes > 0) {return minutes + "分钟" + seconds + "秒";} else {return seconds + "秒";}}/*** 格式化为可读的描述*/public static String formatReadable(Duration duration) {long days = duration.toDays();long hours = duration.toHoursPart();      // JDK 9+long minutes = duration.toMinutesPart();  // JDK 9+StringBuilder sb = new StringBuilder();if (days > 0) sb.append(days).append("天");if (hours > 0) sb.append(hours).append("小时");if (minutes > 0) sb.append(minutes).append("分钟");return sb.length() > 0 ? sb.toString() : "0分钟";}
}// 使用
Duration duration = Duration.ofHours(2).plusMinutes(30).plusSeconds(45);System.out.println(DurationFormatter.formatHMS(duration));
// 输出:02:30:45System.out.println(DurationFormatter.formatChinese(duration));
// 输出:2小时30分钟45秒System.out.println(DurationFormatter.formatReadable(duration));
// 输出:2小时30分钟

JDK 8 兼容版本(没有 toXxxPart() 方法):

public static String formatHMS_JDK8(Duration duration) {long totalSeconds = duration.getSeconds();long hours = totalSeconds / 3600;long minutes = (totalSeconds % 3600) / 60;long seconds = totalSeconds % 60;return String.format("%02d:%02d:%02d", hours, minutes, seconds);
}

第七章:实战案例

7.1 案例一:计算方法执行时间

import java.time.Duration;
import java.time.Instant;public class PerformanceMonitor {public static void main(String[] args) {Instant start = Instant.now();// 执行业务逻辑processData();Instant end = Instant.now();Duration duration = Duration.between(start, end);System.out.println("方法执行耗时: " + duration.toMillis() + " 毫秒");// 判断是否超时(假设超时阈值是 5 秒)Duration timeout = Duration.ofSeconds(5);if (duration.compareTo(timeout) > 0) {System.out.println("警告:方法执行超时!");}}private static void processData() {try {Thread.sleep(2000);  // 模拟耗时操作} catch (InterruptedException e) {e.printStackTrace();}}
}

7.2 案例二:计算会议剩余时间

import java.time.Duration;
import java.time.LocalDateTime;public class MeetingTimer {public static void main(String[] args) {// 会议开始时间LocalDateTime meetingStart = LocalDateTime.of(2025, 11, 13, 14, 0);// 会议时长:1.5小时Duration meetingDuration = Duration.ofMinutes(90);// 会议结束时间LocalDateTime meetingEnd = meetingStart.plus(meetingDuration);// 当前时间LocalDateTime now = LocalDateTime.now();// 计算剩余时间if (now.isBefore(meetingEnd)) {Duration remaining = Duration.between(now, meetingEnd);long minutes = remaining.toMinutes();System.out.println("会议还剩 " + minutes + " 分钟");} else {Duration overdue = Duration.between(meetingEnd, now);long minutes = overdue.toMinutes();System.out.println("会议已超时 " + minutes + " 分钟");}}
}

7.3 案例三:缓存过期时间判断

import java.time.Duration;
import java.time.Instant;public class CacheManager {private Instant cachedTime;private String cachedData;// 缓存有效期:5分钟private static final Duration CACHE_VALIDITY = Duration.ofMinutes(5);public void cache(String data) {this.cachedData = data;this.cachedTime = Instant.now();}public String get() {if (cachedTime == null) {return null;  // 没有缓存}// 计算缓存存活时间Duration age = Duration.between(cachedTime, Instant.now());// 判断是否过期if (age.compareTo(CACHE_VALIDITY) > 0) {System.out.println("缓存已过期");cachedData = null;cachedTime = null;return null;}// 显示剩余有效时间Duration remaining = CACHE_VALIDITY.minus(age);System.out.println("缓存还有 " + remaining.toSeconds() + " 秒过期");return cachedData;}public static void main(String[] args) throws InterruptedException {CacheManager cache = new CacheManager();cache.cache("Hello World");Thread.sleep(2000);System.out.println(cache.get());  // 缓存还有 178 秒过期Thread.sleep(300000);  // 5分钟后System.out.println(cache.get());  // 缓存已过期}
}

7.4 案例四:计算工作日的工作时长

import java.time.Duration;
import java.time.LocalTime;public class WorkHoursCalculator {public static void main(String[] args) {// 上班时间LocalTime clockIn = LocalTime.of(9, 0);// 下班时间LocalTime clockOut = LocalTime.of(18, 30);// 午休时间Duration lunchBreak = Duration.ofMinutes(60);// 计算总工作时长Duration totalAtWork = Duration.between(clockIn, clockOut);Duration actualWork = totalAtWork.minus(lunchBreak);long hours = actualWork.toHours();long minutes = actualWork.toMinutesPart();  // JDK 9+System.out.println("今天工作了 " + hours + " 小时 " + minutes + " 分钟");// 输出:今天工作了 8 小时 30 分钟// 一周工作时长Duration weeklyWork = actualWork.multipliedBy(5);System.out.println("一周工作 " + weeklyWork.toHours() + " 小时");// 输出:一周工作 42 小时}
}

7.5 案例五:视频播放进度

import java.time.Duration;public class VideoPlayer {private Duration videoDuration;  // 视频总时长private Duration currentPosition;  // 当前播放位置public VideoPlayer(Duration videoDuration) {this.videoDuration = videoDuration;this.currentPosition = Duration.ZERO;}// 播放指定时长public void play(Duration duration) {currentPosition = currentPosition.plus(duration);if (currentPosition.compareTo(videoDuration) > 0) {currentPosition = videoDuration;}printProgress();}// 快进public void forward(Duration duration) {play(duration);}// 快退public void rewind(Duration duration) {currentPosition = currentPosition.minus(duration);if (currentPosition.isNegative()) {currentPosition = Duration.ZERO;}printProgress();}// 显示进度private void printProgress() {long currentSec = currentPosition.toSeconds();long totalSec = videoDuration.toSeconds();double percentage = (double) currentSec / totalSec * 100;System.out.printf("播放进度: %02d:%02d / %02d:%02d (%.1f%%)%n",currentPosition.toHoursPart(), currentPosition.toMinutesPart(),videoDuration.toHoursPart(), videoDuration.toMinutesPart(),percentage);}public static void main(String[] args) {// 视频总时长 2 小时VideoPlayer player = new VideoPlayer(Duration.ofHours(2));// 播放 30 分钟player.play(Duration.ofMinutes(30));  // 00:30 / 02:00 (25.0%)// 快进 15 分钟player.forward(Duration.ofMinutes(15));  // 00:45 / 02:00 (37.5%)// 快退 10 分钟player.rewind(Duration.ofMinutes(10));  // 00:35 / 02:00 (29.2%)}
}

第八章:Duration vs Period

8.1 Duration 和 Period 的区别

Java 8 引入了两个表示时间段的类,很容易混淆:

对比项DurationPeriod
表示基于时间的时间段基于日期的时间段
单位秒、毫秒、纳秒年、月、日
精度纳秒级天级
用途时分秒的计算年月日的计算
适用场景倒计时、性能监控、时间间隔年龄计算、日期间隔

简单记忆:

  • Duration:精确到秒的时间段,比如"2小时30分"
  • Period:日历上的时间段,比如"3年2个月5天"

8.2 使用对比

import java.time.Duration;
import java.time.Period;
import java.time.LocalDate;
import java.time.LocalDateTime;// Duration:时分秒
Duration duration = Duration.ofHours(2).plusMinutes(30);
System.out.println(duration);  // PT2H30M// Period:年月日
Period period = Period.ofYears(1).plusMonths(2).plusDays(15);
System.out.println(period);  // P1Y2M15D// Duration:计算两个时间点的间隔(时分秒)
LocalDateTime dt1 = LocalDateTime.of(2025, 11, 13, 10, 0);
LocalDateTime dt2 = LocalDateTime.of(2025, 11, 13, 15, 30);
Duration dur = Duration.between(dt1, dt2);
System.out.println("相差: " + dur.toHours() + " 小时");  // 5 小时// Period:计算两个日期的间隔(年月日)
LocalDate date1 = LocalDate.of(2020, 1, 1);
LocalDate date2 = LocalDate.of(2025, 11, 13);
Period per = Period.between(date1, date2);
System.out.println("相差: " + per.getYears() + " 年 "+ per.getMonths() + " 月 " + per.getDays() + " 天");
// 5 年 10 月 12 天

8.3 何时使用 Duration?

使用 Duration 的场景:

  • ✅ 计算方法执行时间
  • ✅ 倒计时(精确到秒)
  • ✅ 超时判断
  • ✅ 性能监控
  • ✅ 两个时间点的间隔(小时/分钟/秒)
  • ✅ 视频/音频播放进度

使用 Period 的场景:

  • ✅ 计算年龄
  • ✅ 计算两个日期相差多少年/月/日
  • ✅ 订阅周期(1个月、1年等)
  • ✅ 日期间隔(不关心具体时间)

第九章:与旧 API 对比

9.1 旧的方式(Date + Calendar)

以前计算两个时间的差值,代码很丑:

import java.util.Date;// 旧方式:使用 Date
Date start = new Date();
// ... 执行操作
Date end = new Date();long diffMillis = end.getTime() - start.getTime();  // 毫秒差
long diffSeconds = diffMillis / 1000;               // 秒
long diffMinutes = diffSeconds / 60;                // 分钟
long diffHours = diffMinutes / 60;                  // 小时System.out.println("耗时: " + diffMillis + " 毫秒");

9.2 新的方式(Instant + Duration)

现在简洁多了:

import java.time.Instant;
import java.time.Duration;// 新方式:使用 Instant + Duration
Instant start = Instant.now();
// ... 执行操作
Instant end = Instant.now();Duration duration = Duration.between(start, end);System.out.println("耗时: " + duration.toMillis() + " 毫秒");
System.out.println("耗时: " + duration.toSeconds() + " 秒");
System.out.println("耗时: " + duration.toMinutes() + " 分钟");

优势:

  • 代码更简洁
  • 类型安全(不会混淆毫秒和秒)
  • 方法语义清晰
  • 不可变,线程安全

9.3 转换:Date ↔ Instant

如果你的老代码用的是 Date,可以这样转换:

import java.util.Date;
import java.time.Instant;
import java.time.Duration;// Date 转 Instant
Date date = new Date();
Instant instant = date.toInstant();// Instant 转 Date
Instant instant2 = Instant.now();
Date date2 = Date.from(instant2);// 计算两个 Date 之间的 Duration
Date start = new Date();
Thread.sleep(1000);
Date end = new Date();Duration duration = Duration.between(start.toInstant(), end.toInstant());
System.out.println("耗时: " + duration.toMillis() + " 毫秒");

第十章:最佳实践与注意事项

10.1 不可变性

Duration 是不可变的,所有修改方法都返回新对象:

Duration d1 = Duration.ofHours(2);
Duration d2 = d1.plusMinutes(30);  // 返回新对象System.out.println(d1.toMinutes());  // 120(原对象未变)
System.out.println(d2.toMinutes());  // 150(新对象)

注意:不要忘了接收返回值!

// 错误:修改不生效
Duration duration = Duration.ofHours(1);
duration.plusMinutes(30);  // 返回值被丢弃了!
System.out.println(duration.toMinutes());  // 还是 60// 正确
Duration duration = Duration.ofHours(1);
duration = duration.plusMinutes(30);  // 重新赋值
System.out.println(duration.toMinutes());  // 90

10.2 负数 Duration

Duration 可以是负数,表示"之前"的时间:

Duration past = Duration.ofHours(-2);  // -2小时
System.out.println(past.isNegative());  // true// 计算时间差可能得到负数
LocalDateTime future = LocalDateTime.now().plusHours(2);
LocalDateTime now = LocalDateTime.now();Duration diff = Duration.between(future, now);  // 从未来到现在
System.out.println(diff.isNegative());  // true(负数)// 如果需要绝对值
Duration absolute = diff.abs();
System.out.println(absolute.isNegative());  // false

10.3 精度问题

Duration 的精度是纳秒,但:

// 从 LocalDate 创建 Duration 会丢失精度
LocalDate date1 = LocalDate.of(2025, 11, 13);
LocalDate date2 = LocalDate.of(2025, 11, 14);// 错误:LocalDate 不支持 Duration.between
// Duration d = Duration.between(date1, date2);  // 编译错误// 正确:使用 Period
Period period = Period.between(date1, date2);
System.out.println(period.getDays());  // 1

记住

  • Duration 用于 InstantLocalDateTimeLocalTime
  • Period 用于 LocalDate

10.4 性能考虑

Duration 创建开销很小,可以频繁使用:

// 不用担心性能,Duration 很轻量
for (int i = 0; i < 1_000_000; i++) {Duration d = Duration.ofSeconds(i);// ...
}

但如果需要缓存固定值,可以使用常量:

public class TimeConstants {public static final Duration TIMEOUT_5_SECONDS = Duration.ofSeconds(5);public static final Duration CACHE_TTL = Duration.ofMinutes(30);public static final Duration SESSION_TIMEOUT = Duration.ofHours(2);
}

10.5 与 ChronoUnit 配合

有时候用 ChronoUnit 更方便:

import java.time.temporal.ChronoUnit;// 使用 ChronoUnit
Duration d1 = Duration.of(5, ChronoUnit.SECONDS);  // 5秒
Duration d2 = Duration.of(30, ChronoUnit.MINUTES); // 30分钟// 等价于
Duration d3 = Duration.ofSeconds(5);
Duration d4 = Duration.ofMinutes(30);

第十一章:常见问题

Q1: Duration 能表示月和年吗?

不能。Duration 基于秒,无法准确表示"1个月"(因为每月天数不同)。

如果需要月和年,用 Period

// 错误:Duration 没有 ofMonths
// Duration d = Duration.ofMonths(1);  // 编译错误// 正确:使用 Period
Period period = Period.ofMonths(1);

Q2: 如何格式化 Duration 为 “HH:MM:SS”?

Duration 没有内置格式化,需要手动处理(参考第六章):

public static String format(Duration duration) {long hours = duration.toHours();long minutes = duration.toMinutesPart();  // JDK 9+long seconds = duration.toSecondsPart();  // JDK 9+return String.format("%02d:%02d:%02d", hours, minutes, seconds);
}

Q3: Duration 和 long 毫秒值哪个好?

推荐 Duration

// 不推荐:用 long 存毫秒
long timeoutMillis = 5000;  // 5秒?5分钟?不明确// 推荐:用 Duration,语义清晰
Duration timeout = Duration.ofSeconds(5);

Q4: 如何判断 Duration 是否超过某个值?

Duration actual = Duration.ofSeconds(10);
Duration limit = Duration.ofSeconds(5);if (actual.compareTo(limit) > 0) {System.out.println("超过限制");
}// 或者封装工具方法
if (actual.toSeconds() > limit.toSeconds()) {System.out.println("超过限制");
}

Q5: Duration 可以序列化吗?

可以,Duration 实现了 Serializable

JSON 序列化(Jackson):

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());  // 注册 Java 8 时间模块Duration duration = Duration.ofHours(2);
String json = mapper.writeValueAsString(duration);
System.out.println(json);  // "PT2H"Duration parsed = mapper.readValue(json, Duration.class);

第十二章:总结

12.1 核心要点

  1. Duration 表示时间段:基于秒和纳秒,精确到纳秒级
  2. 不可变类:所有修改方法返回新对象
  3. 丰富的创建方法ofSeconds()ofMinutes()between()
  4. 方便的计算:加减乘除、取反、绝对值
  5. 灵活的转换:转换为秒、分钟、小时等
  6. 适用场景:性能监控、超时判断、时间间隔计算

12.2 常用方法速查

创建:

  • Duration.ofSeconds(long)
  • Duration.ofMinutes(long)
  • Duration.ofHours(long)
  • Duration.between(Temporal, Temporal)
  • Duration.parse(String)

获取值:

  • toSeconds() - 总秒数
  • toMinutes() - 总分钟数
  • toHours() - 总小时数
  • toMillis() - 总毫秒数
  • toSecondsPart() - 秒部分(JDK 9+)
  • toMinutesPart() - 分钟部分(JDK 9+)

计算:

  • plus(Duration) / plusXxx(long) - 加法
  • minus(Duration) / minusXxx(long) - 减法
  • multipliedBy(long) - 乘法
  • dividedBy(long) - 除法
  • negated() - 取反
  • abs() - 绝对值

比较:

  • compareTo(Duration) - 比较大小
  • equals(Object) - 判断相等
  • isZero() - 是否为零
  • isNegative() - 是否为负数

12.3 使用建议

  1. 优先使用 Duration:比 long 毫秒值更清晰
  2. 使用常量:频繁使用的 Duration 定义为常量
  3. 注意不可变性:记得接收返回值
  4. 选对类型:时分秒用 Duration,年月日用 Period
  5. 链式调用:利用不可变性优雅地链式计算

12.4 下一步

掌握了 Duration 后,可以继续学习:

  1. Period:处理年月日的时间段
  2. Instant:时间戳
  3. LocalDateTime:本地日期时间
  4. ZonedDateTime:带时区的日期时间
  5. DateTimeFormatter:日期时间格式化

结语

Duration 是 Java 8 时间 API 的重要组成部分,它让时间计算变得简单而优雅。

以前我们用 System.currentTimeMillis() 计算耗时,代码又长又容易出错。现在有了 Duration,一切都变得如此简单:

Instant start = Instant.now();
// 业务逻辑
Duration duration = Duration.between(start, Instant.now());
System.out.println("耗时: " + duration.toMillis() + "ms");

记住:Duration 是你处理时间间隔的好伙伴。性能监控、超时判断、倒计时、时间计算,有了它都不在话下。

最后送你一句话:时间是宝贵的,但处理时间的代码不应该复杂。用好 Duration,让时间计算变得轻松愉快!

http://www.dtcms.com/a/611713.html

相关文章:

  • 网站投放网站建设设计指标
  • memcpy((mem->memory[mem->size]), data, data_size)什么意思
  • 白银市建设局网站首页苏州网站搜索优化
  • 能打开任何网站浏览器wordpress 吾爱破解
  • 微信网站案例长沙城市建设档案馆网站
  • 撤销个人网站备案企业网站托管注意事项
  • 需要推销自己做网站的公司网站建设小程序开发
  • 《C++探幽:仿函数与lambda表达式》
  • python与c++中“类方法“继承再拓展的区别
  • 电商设计素材网站推荐网络推广网站的方法
  • 制作外贸网站的公司简介最新小组排名
  • 数字孪生轻量化加载!移动端如何流畅操控巨型工程?
  • 高明铝业网站建站z怎么做优惠券网站
  • Linux:动态链接与静态链接以及动态库与静态库
  • 如何来构建一个成交型网站企业品牌vi设计
  • 深圳网站建设快速排名自适应网站可以做伪静态页面吗
  • 学网站建设的工资高吗软件网站关键词优化
  • 代理记账 营销型网站沈阳seo博客
  • 有谁用2008做网站服务器施工企业会计王玉红课后答案
  • 【教学类-98-04】20251114《动物花花衣》只有轮廓的动物
  • 获取网站访客qq 原理wordpress mysql 5.7
  • C语言--结构体(Struct)
  • 网站建设忄金手指专业简单aspx网站开发
  • wordpress 导航网站模板设计新颖的网站建站
  • 一起做网店一样的网站北京建设高端网站
  • openEuler 22.03 ARM64 KVM虚拟化安装
  • 西安知名的网站建设公司wordpress登录失败
  • 如何在会议决策错误后进行纠正与说明
  • 哪个网站免费h5模板多网络推广外包注意哪些
  • 网站的布局怎么做采集文章留在网站