Java Duration

Duration 是 Java 8 引入的類別,用於表示兩個時間點之間的時間間隔,以秒和奈秒為單位。適合處理基於時間的間隔(時、分、秒)。

引入套件

import java.time.Duration;

建立 Duration

使用 of 方法

// 指定天數
Duration days = Duration.ofDays(5);        // PT120H (5天 = 120小時)

// 指定小時
Duration hours = Duration.ofHours(8);      // PT8H

// 指定分鐘
Duration minutes = Duration.ofMinutes(30); // PT30M

// 指定秒數
Duration seconds = Duration.ofSeconds(90); // PT1M30S

// 指定毫秒
Duration millis = Duration.ofMillis(500);  // PT0.5S

// 指定奈秒
Duration nanos = Duration.ofNanos(1000000); // PT0.001S

使用 of 方法搭配 ChronoUnit

Duration d1 = Duration.of(5, ChronoUnit.HOURS);   // PT5H
Duration d2 = Duration.of(30, ChronoUnit.MINUTES); // PT30M
Duration d3 = Duration.of(1, ChronoUnit.DAYS);    // PT24H

從字串解析

// ISO-8601 格式:PT[n]H[n]M[n]S
Duration d1 = Duration.parse("PT2H30M");      // 2小時30分
Duration d2 = Duration.parse("PT1H30M45S");   // 1小時30分45秒
Duration d3 = Duration.parse("PT0.5S");       // 0.5秒
Duration d4 = Duration.parse("P2DT3H");       // 2天3小時

計算兩個時間的間隔

LocalTime start = LocalTime.of(9, 0);
LocalTime end = LocalTime.of(17, 30);

Duration duration = Duration.between(start, end);
System.out.println(duration);  // PT8H30M

// 也可用於 LocalDateTime、Instant 等
Instant t1 = Instant.now();
// ... 執行操作 ...
Instant t2 = Instant.now();
Duration elapsed = Duration.between(t1, t2);

取得 Duration 資訊

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

// 取得總量
long totalSeconds = duration.getSeconds();  // 9045
int nanos = duration.getNano();             // 0

// 取得各單位的值(Java 9+)
long days = duration.toDays();           // 0
long hours = duration.toHours();         // 2
long minutes = duration.toMinutes();     // 150
long seconds = duration.toSeconds();     // 9045
long millis = duration.toMillis();       // 9045000
long nanoTotal = duration.toNanos();     // 9045000000000

// 取得各部分的值(Java 9+)
int hoursPart = duration.toHoursPart();     // 2
int minutesPart = duration.toMinutesPart(); // 30
int secondsPart = duration.toSecondsPart(); // 45
int millisPart = duration.toMillisPart();   // 0
int nanosPart = duration.toNanosPart();     // 0

Duration 運算

加減運算

Duration d1 = Duration.ofHours(2);
Duration d2 = Duration.ofMinutes(30);

// 加法
Duration sum = d1.plus(d2);           // PT2H30M
Duration plusHours = d1.plusHours(1); // PT3H
Duration plusMins = d1.plusMinutes(15); // PT2H15M

// 減法
Duration diff = d1.minus(d2);          // PT1H30M
Duration minusHours = d1.minusHours(1); // PT1H

乘除運算

Duration d = Duration.ofMinutes(30);

Duration doubled = d.multipliedBy(2);  // PT1H
Duration halved = d.dividedBy(2);      // PT15M

// 兩個 Duration 相除(Java 9+)
Duration d1 = Duration.ofHours(2);
Duration d2 = Duration.ofMinutes(30);
long ratio = d1.dividedBy(d2);  // 4

取絕對值和取反

Duration negative = Duration.ofHours(-2);

Duration absolute = negative.abs();    // PT2H
Duration negated = negative.negated(); // PT2H

Duration positive = Duration.ofHours(2);
Duration neg = positive.negated();     // PT-2H

Duration 比較

Duration d1 = Duration.ofHours(2);
Duration d2 = Duration.ofMinutes(90);

int result = d1.compareTo(d2);  // 正數(d1 較長)

boolean isZero = d1.isZero();       // false
boolean isNegative = d1.isNegative(); // false
boolean isPositive = !d1.isNegative() && !d1.isZero(); // true

// 快速比較(Java 18+)
// boolean isPositive = d1.isPositive();

應用於時間運算

LocalDateTime now = LocalDateTime.now();
Duration duration = Duration.ofHours(2).plusMinutes(30);

// 加上 Duration
LocalDateTime later = now.plus(duration);

// 減去 Duration
LocalDateTime earlier = now.minus(duration);

// 用於 Instant
Instant instant = Instant.now();
Instant futureInstant = instant.plus(Duration.ofDays(1));

實用範例

格式化顯示

public static String formatDuration(Duration duration) {
    long hours = duration.toHours();
    int minutes = duration.toMinutesPart();
    int seconds = duration.toSecondsPart();
    
    if (hours > 0) {
        return String.format("%d 小時 %d 分 %d 秒", hours, minutes, seconds);
    } else if (minutes > 0) {
        return String.format("%d 分 %d 秒", minutes, seconds);
    } else {
        return String.format("%d 秒", seconds);
    }
}

Duration d = Duration.ofSeconds(3725);
System.out.println(formatDuration(d));  // 1 小時 2 分 5 秒

執行時間計算

public static <T> T measureTime(Supplier<T> task, String taskName) {
    Instant start = Instant.now();
    T result = task.get();
    Duration elapsed = Duration.between(start, Instant.now());
    System.out.printf("%s 耗時: %d ms%n", taskName, elapsed.toMillis());
    return result;
}

// 使用
String result = measureTime(() -> {
    // 模擬耗時操作
    try { Thread.sleep(1500); } catch (Exception e) {}
    return "完成";
}, "資料處理");

倒數計時

public static void countdown(Duration duration) throws InterruptedException {
    Instant endTime = Instant.now().plus(duration);
    
    while (Instant.now().isBefore(endTime)) {
        Duration remaining = Duration.between(Instant.now(), endTime);
        System.out.printf("\r剩餘時間: %02d:%02d:%02d",
            remaining.toHours(),
            remaining.toMinutesPart(),
            remaining.toSecondsPart()
        );
        Thread.sleep(1000);
    }
    System.out.println("\n時間到!");
}

// 5分鐘倒數
countdown(Duration.ofMinutes(5));

工作時數計算

public static Duration calculateWorkHours(LocalTime clockIn, LocalTime clockOut, Duration breakTime) {
    Duration totalTime = Duration.between(clockIn, clockOut);
    return totalTime.minus(breakTime);
}

LocalTime in = LocalTime.of(9, 0);
LocalTime out = LocalTime.of(18, 0);
Duration lunch = Duration.ofHours(1);

Duration workHours = calculateWorkHours(in, out, lunch);
System.out.println("工作時數: " + workHours.toHours() + " 小時");  // 8 小時

速率限制

public class RateLimiter {
    private final Duration interval;
    private Instant lastAction;
    
    public RateLimiter(Duration interval) {
        this.interval = interval;
        this.lastAction = Instant.EPOCH;
    }
    
    public boolean tryAcquire() {
        Instant now = Instant.now();
        if (Duration.between(lastAction, now).compareTo(interval) >= 0) {
            lastAction = now;
            return true;
        }
        return false;
    }
    
    public Duration getWaitTime() {
        Duration elapsed = Duration.between(lastAction, Instant.now());
        Duration wait = interval.minus(elapsed);
        return wait.isNegative() ? Duration.ZERO : wait;
    }
}

// 每秒最多執行一次
RateLimiter limiter = new RateLimiter(Duration.ofSeconds(1));
if (limiter.tryAcquire()) {
    System.out.println("執行操作");
} else {
    System.out.println("請等待 " + limiter.getWaitTime().toMillis() + " ms");
}

Duration vs Period

特性DurationPeriod
單位秒、奈秒年、月、日
精度奈秒級天級
用途時間間隔(時分秒)日期間隔(年月日)
適用類型LocalTime, InstantLocalDate
// Duration:時間間隔
Duration duration = Duration.ofHours(5);
LocalTime time = LocalTime.of(10, 0);
LocalTime newTime = time.plus(duration);  // 15:00

// Period:日期間隔
Period period = Period.ofMonths(2);
LocalDate date = LocalDate.of(2024, 1, 15);
LocalDate newDate = date.plus(period);  // 2024-03-15

重點整理

  • Duration 表示基於時間的間隔(秒和奈秒)
  • 使用 ofXxx() 方法建立各種時間長度
  • 使用 between() 計算兩個時間點的間隔
  • 支援加減乘除運算
  • 不可變(immutable)類別
  • 適合用於 LocalTimeInstantLocalDateTime
  • 日期間隔(年月日)應使用 Period