CodeGym /课程 /JAVA 25 SELF /日期计算与比较、Duration、Period

日期计算与比较、Duration、Period

JAVA 25 SELF
第 13 级 , 课程 5
可用

1. 日期与时间的加减

在编程中,处理日期和时间并不仅仅是“把日期漂亮地输出到屏幕”。我们经常需要:

  • 知道距离重要事件还有多少天;
  • 计算用户年龄;
  • 确定两个事件之间过去了多少时间;
  • 检查截止日期是否已到;
  • 在日期上加上一定数量的天、周、月或小时。

例如,在我们的应用中想提醒用户距离任务的截止日期还有 3 天;或者在生日已过(日期已在过去)时向他表示祝贺。

为此,java.time 提供了专门的类和方法,使这些计算简单且安全。

加法与减法的基本方法

所有日期与时间类(LocalDateLocalTimeLocalDateTimeZonedDateTimeInstant)都有以下方法:

  • plusXxx() — 相加(天、月、年、小时、分钟等)
  • minusXxx() — 相减(同上)
import java.time.LocalDate;

LocalDate today = LocalDate.now(); // 今天的日期
LocalDate tomorrow = today.plusDays(1); // 明天
LocalDate nextMonth = today.plusMonths(1); // 一个月后
LocalDate lastWeek = today.minusWeeks(1); // 一周前

System.out.println("今天: " + today);
System.out.println("明天: " + tomorrow);
System.out.println("一个月后: " + nextMonth);
System.out.println("一周前: " + lastWeek);

所有这些方法都会返回一个新对象,而不会修改原对象。不可变性——一切的基础!

import java.time.LocalTime;

LocalTime now = LocalTime.now();
LocalTime inTwoHours = now.plusHours(2);
LocalTime tenMinutesAgo = now.minusMinutes(10);

System.out.println("现在: " + now);
System.out.println("2 小时后: " + inTwoHours);
System.out.println("10 分钟前: " + tenMinutesAgo);

组合方法

LocalDate vacation = today.plusMonths(2).plusDays(10);
System.out.println("休假: " + vacation);

2. 获取日期差值:Period 与 Duration

当需要知道两个日期或两个时刻之间经过了多久时,两个主角登场——PeriodDuration。让我们更近地了解它们。

Period 与 Duration 有何不同?

  • Period — 用于按日期(年、月、日)处理。例如,“3 年 2 个月 5 天”。
  • Duration — 用于按时间(小时、分钟、秒、纳秒)处理。例如,“5 小时 30 分钟”。
用途 用法示例
Period
日期之间的差值 两日期之间的年/月/日数量
Duration
时刻之间的差值 两事件之间的小时/分钟/秒

示例:计算用户年龄

import java.time.LocalDate;
import java.time.Period;

LocalDate birthday = LocalDate.of(2000, 1, 15); // 生日
LocalDate today = LocalDate.now();

Period age = Period.between(birthday, today);

System.out.println("年龄: " + age.getYears() + " 年, " +
                   age.getMonths() + " 个月, " +
                   age.getDays() + " 天");

类比:Period 就像“人类可读”的日期差(“我 24 年 5 个月 12 天大”)。

示例:距离截止日期还剩多久

import java.time.LocalDate;

LocalDate deadline = LocalDate.of(2025, 7, 1);
LocalDate today = LocalDate.now();

if (today.isBefore(deadline)) {
    Period left = Period.between(today, deadline);
    System.out.println("距离截止日期还剩: " +
        left.getMonths() + " 个月和 " +
        left.getDays() + " 天");
} else {
    System.out.println("截止日期已过!");
}

Duration:时间差

import java.time.LocalDateTime;
import java.time.Duration;

LocalDateTime start = LocalDateTime.of(2025, 6, 1, 10, 0, 0);
LocalDateTime end = LocalDateTime.of(2025, 6, 1, 15, 30, 0);

Duration duration = Duration.between(start, end);

System.out.println("持续时间: " + duration.toHours() + " 小时 " +
                   (duration.toMinutes() % 60) + " 分钟");

Duration 以秒、分钟、小时、天来度量(其中“天”始终按 24 小时计算,不考虑日历特性)。

对比:什么时候用 Period,什么时候用 Duration?

  • 如果只有日期(没有时间)——使用 Period
  • 如果包含时间(例如 LocalDateTimeInstant)——使用 Duration

3. 日期与时间比较:isBefore、isAfter、equals

有时只需要判断:日期是否已经到来?事件在过去还是未来?为此,所有日期与时间类都提供了这些方法:

  • isBefore(otherDate)
  • isAfter(otherDate)
  • isEqual(otherDate)(或直接 equals
import java.time.LocalDate;

LocalDate today = LocalDate.now();
LocalDate deadline = LocalDate.of(2025, 7, 1);

if (today.isBefore(deadline)) {
    System.out.println("还有时间!");
} else if (today.isEqual(deadline)) {
    System.out.println("今天是截止日期!");
} else {
    System.out.println("截止日期已经过去了 :(");
}
import java.time.LocalTime;

LocalTime now = LocalTime.now();
LocalTime lunch = LocalTime.of(13, 0);

if (now.isAfter(lunch)) {
    System.out.println("已经吃过午饭了!");
} else {
    System.out.println("离午饭还有时间。");
}

重要:只能比较相同类型的对象(LocalDateLocalDateLocalDateTimeLocalDateTime,等等)。

4. 实践:计算差值与检查期限

示例:距离任务截止日期还剩多少天

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

LocalDate today = LocalDate.now();
LocalDate deadline = LocalDate.of(2025, 7, 1);

long daysLeft = ChronoUnit.DAYS.between(today, deadline);

if (daysLeft > 0) {
    System.out.println("距离截止日期还有 " + daysLeft + " 天");
} else if (daysLeft == 0) {
    System.out.println("今天是截止日期!");
} else {
    System.out.println("截止日期已过 " + Math.abs(daysLeft) + " 天");
}

ChronoUnit.DAYS.between() —— 获取两日期之间精确天数的便捷方式(不考虑月份和年份)。

示例:两个事件之间相隔多少秒

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

LocalDateTime start = LocalDateTime.of(2025, 6, 1, 10, 0, 0);
LocalDateTime end = LocalDateTime.of(2025, 6, 1, 15, 30, 0);

long seconds = ChronoUnit.SECONDS.between(start, end);

System.out.println("两次事件之间经过了 " + seconds + " 秒");

示例:计算任务的“年龄”

import java.time.LocalDate;
import java.time.Period;

LocalDate created = LocalDate.of(2025, 5, 20);
LocalDate today = LocalDate.now();

Period period = Period.between(created, today);

System.out.println("任务创建于 " +
    period.getDays() + " 天, " +
    period.getMonths() + " 个月和 " +
    period.getYears() + " 年前。");

示例:两个 Instant 之间的持续时间

import java.time.Instant;
import java.time.Duration;

Instant start = Instant.now();
// ... 做点什么 ...
Instant end = Instant.now();

Duration duration = Duration.between(start, end);

System.out.println("执行耗时 " + duration.toMillis() + " 毫秒");

5. 使用 Period 与 Duration 的注意事项

Period 的特点

  • Period 按日历计算差值:2 月 28 日与 3 月 1 日之间是 1 天,即使跨月。
  • 可以分别获取年、月、日:getYears()getMonths()getDays()
  • 如果需要两日期之间的总天数——使用 ChronoUnit.DAYS.between()

Duration 的特点

  • Duration 处理精确时间(小时、分钟、秒)。
  • 可与 LocalTimeLocalDateTimeInstant 一起使用。
  • 不能用于仅日期的 LocalDate —— 否则会抛出异常。

示例:Period 与 Duration 的差异

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Duration;
import java.time.Period;

LocalDate date1 = LocalDate.of(2025, 2, 28);
LocalDate date2 = LocalDate.of(2025, 3, 1);

Period period = Period.between(date1, date2); // 2 天(闰年)
long days = java.time.temporal.ChronoUnit.DAYS.between(date1, date2); // 2 天

System.out.println("Period: " + period.getDays() + " 天");
System.out.println("ChronoUnit: " + days + " 天");

LocalDateTime dt1 = LocalDateTime.of(2025, 6, 1, 23, 0);
LocalDateTime dt2 = LocalDateTime.of(2025, 6, 2, 1, 0);

Duration duration = Duration.between(dt1, dt2); // 2 小时
System.out.println("Duration: " + duration.toHours() + " 小时");

6. 用 Duration 与 Period 以友好格式输出差值

当为用户展示日期或时间差时,“123456 秒”并不直观。通常我们更希望看到类似“2 天 3 小时 15 分钟”。

示例:输出日期与时间的差值

import java.time.LocalDateTime;
import java.time.Duration;

LocalDateTime start = LocalDateTime.of(2025, 6, 1, 14, 0);
LocalDateTime end = LocalDateTime.of(2025, 6, 3, 16, 30);

Duration duration = Duration.between(start, end);

long days = duration.toDays();
long hours = duration.minusDays(days).toHours();
long minutes = duration.minusDays(days).minusHours(hours).toMinutes();

System.out.println("差异: " + days + " 天 " +
    hours + " 小时 " + minutes + " 分钟");

7. 使用 ZonedDateTime 与 Instant 的注意点

  • 不同时区的两个 ZonedDateTime 之间的差值按绝对时间计算,而不是各自时区的“本地时间”。
  • 夏令时/冬令时的切换会影响两个日期之间的小时差。

示例:ZonedDateTime 之间的差值

import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.time.Duration;

ZonedDateTime MinskTime = ZonedDateTime.of(2025, 6, 1, 12, 0, 0, 0, ZoneId.of("Europe/Minsk"));
ZonedDateTime nyTime = ZonedDateTime.of(2025, 6, 1, 5, 0, 0, 0, ZoneId.of("America/New_York"));

Duration diff = Duration.between(nyTime, MinskTime);
System.out.println("差值: " + diff.toHours() + " 小时");

8. 进行日期与时间计算时的常见错误

错误 №1:混淆 Period 和 Duration。
如果尝试把 Duration 用于 LocalDate,会得到异常。处理日期请用 Period,处理时间请用 Duration

错误 №2:忽略时区。
在不同时区比较日期与时间,可能得到意料之外的结果。最好统一到同一时区,或使用 Instant

错误 №3:以为 plus/minus 方法会修改对象本身。
所有 java.time 对象都是不可变的!别忘了把结果赋给新变量(例如 today = today.plusDays(1))。

错误 №4:对 Period 使用 getDays(),并误以为它代表总天数。
实际上它只是扣除年和月后的“剩余天数”。若要获取总天数,请使用 ChronoUnit.DAYS.between()

错误 №5:比较不同类型的对象。
方法 isBeforeisAfterisEqual 只适用于比较相同类型的对象(例如 LocalDateLocalDate)。

1
调查/小测验
日期和时间第 13 级,课程 5
不可用
日期和时间
日期、时间和时区
评论 (1)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
级别 16,-,Hong Kong
2 八月 2026
熬了4节课,终于又能喝鸡汤了🤤