首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 計算時間差完整指南:三種核心方法與日期時間 API 現代化實踐

     編輯:Java 相關知識

核心需求

給定兩個日期時間,計算差值並格式化為「X天X小時X分X秒」。

方法一:Date.getTime() 毫秒差值(傳統基礎版)

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TimeDiffLegacy {
    public static void main(String[] args) {
        String startStr = "2004-01-02 11:30:24";
        String endStr   = "2004-03-26 13:31:40";

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            Date start = sdf.parse(startStr);
            Date end   = sdf.parse(endStr);

            long diffMs = end.getTime() - start.getTime(); // 毫秒差
            printDiff(diffMs);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

    private static void printDiff(long diffMs) {
        long seconds = diffMs / 1000;
        long days    = seconds / (24 * 3600);
        long hours   = (seconds % (24 * 3600)) / 3600;
        long minutes = (seconds % 3600) / 60;
        long secs    = seconds % 60;

        System.out.printf("差值:%d天 %d小時 %d分 %d秒%n", days, hours, minutes, secs);
        // 輸出:差值:83天 2小時 1分 16秒
    }
}

方法二:Calendar 逐欄位運算(舊版進階)

import java.util.Calendar;
import java.util.Date;

public class TimeDiffCalendar {
    public static void main(String[] args) {
        Calendar start = Calendar.getInstance();
        start.set(2004, Calendar.JANUARY, 2, 11, 30, 24); // 月份 0-based
        Calendar end = Calendar.getInstance();
        end.set(2004, Calendar.MARCH, 26, 13, 31, 40);

        long diffMs = end.getTimeInMillis() - start.getTimeInMillis();
        // 同方法一計算天/時/分/秒
    }
}

方法三:Java 8+ java.time 現代 API(強烈推薦)

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

public class TimeDiffModern {
    public static void main(String[] args) {
        String startStr = "2004-01-02 11:30:24";
        String endStr   = "2004-03-26 13:31:40";

        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime start = LocalDateTime.parse(startStr, fmt);
        LocalDateTime end   = LocalDateTime.parse(endStr, fmt);

        // 1. 總差值各單位
        long days    = ChronoUnit.DAYS.between(start, end);
        long hours   = ChronoUnit.HOURS.between(start, end) % 24;
        long minutes = ChronoUnit.MINUTES.between(start, end) % 60;
        long seconds = ChronoUnit.SECONDS.between(start, end) % 60;

        System.out.printf("差值:%d天 %d小時 %d分 %d秒%n", days, hours, minutes, seconds);

        // 2. Duration 物件完整資訊
        java.time.Duration duration = java.time.Duration.between(start, end);
        System.out.println("總毫秒:" + duration.toMillis());
        System.out.println("總秒數:" + duration.getSeconds());
        System.out.println("標準格式:" + duration); // PT83H2M1.16S (ISO-8601)

        // 3. Period 用於日期差(年/月/日)
        java.time.Period period = java.time.Period.between(start.toLocalDate(), end.toLocalDate());
        System.out.printf("日期差:%d年 %d月 %d日%n", period.getYears(), period.getMonths(), period.getDays());
    }
}

三大方法對比

維度Date.getTime()Calendarjava.time (JDK 8+)
API 設計簡單、偏向機器繁瑣、可變、月份 0-based流暢、不可變、領域模型清晰
執行緒安全Date 可變,不安全Calendar 可變,不安全不可變,天然安全
時區處理隱含系統預設隱含系統預設顯式 ZoneId/ZonedDateTime
解析/格式化SimpleDateFormat (不安全)同左DateTimeFormatter (安全)
差值計算手動毫秒換算手動毫秒換算ChronoUnit / Duration / Period 內建
跨夏令時正確性易錯易錯ZonedDateTime 正確處理

進階場景:跨時區、夏令時、業務日曆

// 跨時區:使用 ZonedDateTime
ZoneId taipei = ZoneId.of("Asia/Taipei");
ZoneId ny = ZoneId.of("America/New_York");
ZonedDateTime start = ZonedDateTime.of(2024, 3, 10, 12, 0, 0, 0, taipei);
ZonedDateTime end = ZonedDateTime.of(2024, 3, 10, 20, 0, 0, 0, ny);
Duration d = Duration.between(start, end); // 自動處理時區差與夏令時

// 僅計算工作日(排除週末、假日)— 需自行實作或用三方庫
// 推薦:ThreeTen-Extra (LocalDateRange, BusinessDay) 或 Joda-Time 遺產

常見坑點避雷

  • SimpleDateFormat 非執行緒安全:每次用新建實例,或用 ThreadLocal,或升級 DateTimeFormatter。
  • 月份 0-based:Calendar.JANUARY = 0,LocalDateTime 月份 1-based。
  • 夏令時跳變:Duration.between 計算物理時間差,Period 計算日曆日期差,業務語意不同。
  • 長時間差溢位:int 儲存天數可能溢位,用 long。

總結

  • 舊系統維護:掌握 Date.getTime() 毫秒換算邏輯。
  • 新專案/重構:全面遷移至 java.time,使用 Duration、Period、ChronoUnit 內建方法,程式碼更清晰、安全、正確。
  • 格式化輸出:自行組裝「天/時/分/秒」字串,或用 Duration.format() (JDK 9+) 自定義格式化。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved