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:誤以為 getDays() 是總天數。
事實上它只代表扣掉年與月之後的剩餘天數。若要總天數請使用 ChronoUnit.DAYS.between()

錯誤 №5:比較不同型別的物件。
isBeforeisAfterisEqual 僅適用於相同型別的物件(例如,LocalDateLocalDate)。

1
任務
JAVA 25 SELF, 等級 13, 課堂 5
上鎖
節日倒數計時 🎉
節日倒數計時 🎉
1
任務
JAVA 25 SELF, 等級 13, 課堂 5
上鎖
時光機:檢查日期是否已到 🕰️
時光機:檢查日期是否已到 🕰️
1
任務
JAVA 25 SELF, 等級 13, 課堂 5
上鎖
專案管理:距離截止日還有幾天? 🗓️
專案管理:距離截止日還有幾天? 🗓️
1
任務
JAVA 25 SELF, 等級 13, 課堂 5
上鎖
房地產仲介:計算建築物年齡 🏡
房地產仲介:計算建築物年齡 🏡
1
問卷/小測驗
日期與時間,等級 13,課堂 5
未開放
日期與時間
日期、時間與時區
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION