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

Java 計算任意兩日期間工作日數:無節假日版演算法實現

     編輯:Java 相關知識

演算法核心思路

  1. 計算兩日期總天數 totalDays
  2. 分別找到開始日期和結束日期的下一個星期一,得到 nextMonStart、nextMonEnd
  3. 這兩個星期一之間的天數必能被 7 整除,商即為完整週數 fullWeeks
  4. 完整週工作日 = fullWeeks * 5
  5. 計算開始日期到其下一個星期一的偏移工作日 startOffset(周六日為 0)
  6. 計算結束日期到其下一個星期一的偏移工作日 endOffset(需扣除)
  7. 結果 = fullWeeks*5 + startOffset - endOffset

此法巧妙避開逐日循環,時間複雜度 O(1),支援跨年、跨世紀。

完整可運行代碼

package com.date.util;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

public class WorkingDayCalculator {

    public static void main(String[] args) throws Exception {
        String startStr = "2006-10-01";
        String endStr   = "2006-10-14";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        
        Calendar start = Calendar.getInstance();
        Calendar end   = Calendar.getInstance();
        start.setTime(sdf.parse(startStr));
        end.setTime(sdf.parse(endStr));
        
        WorkingDayCalculator calc = new WorkingDayCalculator();
        System.out.println("開始:" + startStr + " (" + calc.getChineseWeek(start) + ")");
        System.out.println("結束:" + endStr + " (" + calc.getChineseWeek(end) + ")");
        System.out.println("工作日:" + calc.getWorkingDays(start, end));
        System.out.println("休息日:" + calc.getHolidays(start, end));
    }

    // 總天數(支援跨年)
    public int getDaysBetween(Calendar d1, Calendar d2) {
        if (d1.after(d2)) { Calendar t=d1; d1=d2; d2=t; }
        int days = d2.get(Calendar.DAY_OF_YEAR) - d1.get(Calendar.DAY_OF_YEAR);
        int y2 = d2.get(Calendar.YEAR);
        if (d1.get(Calendar.YEAR) != y2) {
            d1 = (Calendar) d1.clone();
            do {
                days += d1.getActualMaximum(Calendar.DAY_OF_YEAR);
                d1.add(Calendar.YEAR, 1);
            } while (d1.get(Calendar.YEAR) != y2);
        }
        return days;
    }

    // 核心:工作日計算
    public int getWorkingDays(Calendar d1, Calendar d2) {
        if (d1.after(d2)) { Calendar t=d1; d1=d2; d2=t; }
        
        int startOffset = calcOffset(d1);
        int endOffset   = calcOffset(d2);
        
        Calendar nextMon1 = getNextMonday(d1);
        Calendar nextMon2 = getNextMonday(d2);
        int fullWeeks = getDaysBetween(nextMon1, nextMon2) / 7;
        
        return fullWeeks * 5 + startOffset - endOffset;
    }

    // 偏移量:日期到下一個星期一的工作日數(周六日=0)
    private int calcOffset(Calendar d) {
        int dayOfWeek = d.get(Calendar.DAY_OF_WEEK); // 1=Sun ... 7=Sat
        int toMon = (9 - dayOfWeek) % 7;             // 到下一個周一的天數
        return (toMon == 0 || toMon == 6) ? 0 : toMon - 1; // 排除周末
    }

    // 獲取下一個星期一
    private Calendar getNextMonday(Calendar d) {
        Calendar c = (Calendar) d.clone();
        do { c.add(Calendar.DATE, 1); } while (c.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY);
        return c;
    }

    public String getChineseWeek(Calendar d) {
        String[] ws = {"星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
        return ws[d.get(Calendar.DAY_OF_WEEK)-1];
    }

    public int getHolidays(Calendar d1, Calendar d2) {
        return getDaysBetween(d1, d2) - getWorkingDays(d1, d2);
    }
}

關鍵細節解析

  • Calendar.DAY_OF_WEEK:1=星期日 … 7=星期六
  • getActualMaximum(DAY_OF_YEAR):正確處理閏年(366 天)
  • 克隆 Calendar 避免副作用修改原對象
  • 偏移量邏輯:toMon = (9 - dow) % 7 統一映射到 0-6,再排除 0(周一) 和 6(周六) 對應的周末情況

擴展:加入法定節假日支援

當前版本不包含法定節假日。擴展方向:

  1. 維護 Set<LocalDate> holidays(含調休工作日)
  2. 計算基礎工作日後,遍歷區間內節假日扣除,調休日加回
  3. 或採用「工作日日曆表」預計算年曆,O(1) 查詢

結語

此演算法以數學推導替代迭代,優雅解決工作日計算問題。代碼簡潔、無第三方依賴、支援跨年,適合嵌入各類業務系統。若需節假日支援,建議引入 java.time (Java 8+) 配合節假日配置表,保持核心邏輯不變。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved