Java Duration 和 Period

Duration 用於表示以秒和奈秒為單位的時間間隔,而 Period 用於表示以年、月、日為單位的日期間隔。

Duration(時間間隔)

適用於時間點之間的精確時間差:

import java.time.Duration;
import java.time.LocalTime;
import java.time.LocalDateTime;
import java.time.Instant;

// 計算兩個時間之間的差距
LocalTime start = LocalTime.of(9, 0);
LocalTime end = LocalTime.of(17, 30);
Duration duration = Duration.between(start, end);

System.out.println(duration);              // PT8H30M
System.out.println(duration.toHours());    // 8
System.out.println(duration.toMinutes());  // 510
System.out.println(duration.getSeconds()); // 30600

建立 Duration

// 從時間單位建立
Duration hours = Duration.ofHours(5);        // PT5H
Duration minutes = Duration.ofMinutes(30);   // PT30M
Duration seconds = Duration.ofSeconds(90);   // PT1M30S
Duration millis = Duration.ofMillis(1500);   // PT1.5S
Duration nanos = Duration.ofNanos(1000000);  // PT0.001S

// 從字串解析
Duration parsed = Duration.parse("PT2H30M");  // 2 小時 30 分鐘

// 組合
Duration combined = Duration.ofHours(2).plusMinutes(30);  // PT2H30M

Duration 運算

Duration duration = Duration.ofHours(5);

// 加減運算
Duration plus = duration.plusMinutes(30);    // PT5H30M
Duration minus = duration.minusHours(1);     // PT4H

// 乘除運算
Duration doubled = duration.multipliedBy(2);  // PT10H
Duration halved = duration.dividedBy(2);      // PT2H30M

// 絕對值
Duration negative = Duration.ofHours(-5);
Duration absolute = negative.abs();  // PT5H

// 取反
Duration negated = duration.negated();  // PT-5H

取得 Duration 資訊

Duration duration = Duration.ofHours(2).plusMinutes(30).plusSeconds(45);

// 各種單位
duration.toDays();       // 0
duration.toHours();      // 2
duration.toMinutes();    // 150
duration.toSeconds();    // 9045
duration.toMillis();     // 9045000
duration.toNanos();      // 9045000000000

// 取得部分
duration.toHoursPart();    // 2
duration.toMinutesPart();  // 30
duration.toSecondsPart();  // 45

// 檢查
duration.isZero();      // false
duration.isNegative();  // false

Duration 應用

// 計算執行時間
Instant start = Instant.now();
// ... 執行任務 ...
Instant end = Instant.now();
Duration elapsed = Duration.between(start, end);
System.out.println("執行時間:" + elapsed.toMillis() + " 毫秒");

// 設定超時
Duration timeout = Duration.ofSeconds(30);
if (waitTime.compareTo(timeout) > 0) {
    throw new TimeoutException("操作超時");
}

// 格式化輸出
public String formatDuration(Duration d) {
    long hours = d.toHours();
    int minutes = d.toMinutesPart();
    int seconds = d.toSecondsPart();
    return String.format("%d:%02d:%02d", hours, minutes, seconds);
}

Period(日期間隔)

適用於日期之間以年月日計算的差距:

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

// 計算兩個日期之間的差距
LocalDate start = LocalDate.of(2020, 1, 1);
LocalDate end = LocalDate.of(2024, 12, 10);
Period period = Period.between(start, end);

System.out.println(period);            // P4Y11M9D
System.out.println(period.getYears());  // 4
System.out.println(period.getMonths()); // 11
System.out.println(period.getDays());   // 9

建立 Period

// 從日期單位建立
Period years = Period.ofYears(2);        // P2Y
Period months = Period.ofMonths(6);      // P6M
Period weeks = Period.ofWeeks(4);        // P28D(轉換為天)
Period days = Period.ofDays(10);         // P10D

// 組合
Period combined = Period.of(1, 6, 15);   // 1年6個月15天

// 從字串解析
Period parsed = Period.parse("P1Y6M15D");

Period 運算

Period period = Period.of(1, 6, 15);

// 加減運算
Period plus = period.plusMonths(3);      // P1Y9M15D
Period minus = period.minusDays(5);      // P1Y6M10D

// 乘法
Period doubled = period.multipliedBy(2); // P2Y12M30D

// 取反
Period negated = period.negated();       // P-1Y-6M-15D

// 標準化(只調整月和年)
Period normalized = Period.of(0, 15, 0).normalized();  // P1Y3M

取得 Period 資訊

Period period = Period.of(2, 8, 15);

// 各部分
period.getYears();   // 2
period.getMonths();  // 8
period.getDays();    // 15

// 總天數(近似值)
period.toTotalMonths();  // 32

// 檢查
period.isZero();      // false
period.isNegative();  // false(任一部分為負則為 true)

Period 應用

// 計算年齡
public int calculateAge(LocalDate birthday) {
    LocalDate today = LocalDate.now();
    Period age = Period.between(birthday, today);
    return age.getYears();
}

// 計算到期日
public LocalDate calculateDueDate(LocalDate startDate, Period validity) {
    return startDate.plus(validity);
}

// 會員有效期
Period membershipPeriod = Period.ofYears(1);
LocalDate expiryDate = LocalDate.now().plus(membershipPeriod);

Duration vs Period

特性DurationPeriod
單位秒、奈秒年、月、日
精確性精確不精確(月份天數不同)
適用時間間隔日期間隔
物件Instant, LocalTime, LocalDateTimeLocalDate, LocalDateTime

使用時機

// Duration:精確時間
Duration workHours = Duration.ofHours(8);
Duration timeout = Duration.ofSeconds(30);
Duration latency = Duration.between(requestTime, responseTime);

// Period:日期概念
Period age = Period.between(birthday, today);
Period subscription = Period.ofMonths(3);
Period warranty = Period.ofYears(2);

實際範例

格式化時間差

public String formatDuration(Duration d) {
    if (d.isNegative()) {
        d = d.negated();
    }

    long days = d.toDays();
    int hours = d.toHoursPart();
    int minutes = d.toMinutesPart();
    int seconds = d.toSecondsPart();

    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(" 分鐘 ");
    if (seconds > 0) sb.append(seconds).append(" 秒");

    return sb.toString().trim();
}

// 使用
Duration d = Duration.ofHours(26).plusMinutes(30);
System.out.println(formatDuration(d));  // "1 天 2 小時 30 分鐘"

格式化年齡

public String formatAge(LocalDate birthday) {
    Period age = Period.between(birthday, LocalDate.now());
    return age.getYears() + " 歲 " + age.getMonths() + " 個月";
}

// 使用
LocalDate birthday = LocalDate.of(1990, 5, 15);
System.out.println(formatAge(birthday));  // "34 歲 7 個月"

倒數計時

public String countdown(LocalDateTime targetTime) {
    Duration remaining = Duration.between(LocalDateTime.now(), targetTime);

    if (remaining.isNegative()) {
        return "已過期";
    }

    long days = remaining.toDays();
    int hours = remaining.toHoursPart();
    int minutes = remaining.toMinutesPart();
    int seconds = remaining.toSecondsPart();

    return String.format("%d 天 %d 小時 %d 分 %d 秒", 
        days, hours, minutes, seconds);
}

工作時數計算

public Duration calculateWorkHours(List<WorkRecord> records) {
    return records.stream()
        .map(r -> Duration.between(r.getStartTime(), r.getEndTime()))
        .reduce(Duration.ZERO, Duration::plus);
}

重點整理

類別用途建立方式運算
Duration時間間隔ofHours, ofMinutes, betweenplus, minus, multipliedBy
Period日期間隔ofYears, ofMonths, betweenplus, minus, multipliedBy
  • Duration 用於精確的時間計算(秒、毫秒)
  • Period 用於日曆概念的日期計算(年、月、日)
  • 使用 between() 計算兩個時間/日期的差距
  • 兩者都是不可變物件,運算會回傳新物件