基于 Java 的工作日與節假日智能識別
作者:一安
在企業級應用開發中,經常需要判斷某個日期是否為工作日或節假日,例如考勤系統、任務調度系統、銀行交易系統等,來看看以下方案。

前言
在企業級應用開發中,經常需要判斷某個日期是否為工作日或節假日,例如考勤系統、任務調度系統、銀行交易系統等。
效果圖

基礎實現方案
最基礎的方案是使用Java內置的日期時間API,結合簡單的周末判斷邏輯:
public class HolidayUtil {
// 法定節假日集合
private static final Set<LocalDate> HOLIDAYS = new HashSet<>();
// 調休工作日集合(周末調休為工作日)
private static final Set<LocalDate> ADJUSTED_WORKDAYS = new HashSet<>();
static {
// 初始化法定節假日
HOLIDAYS.add(LocalDate.of(2025, 1, 1)); // 元旦
HOLIDAYS.add(LocalDate.of(2025, 1, 28)); // 除夕
// 初始化調休工作日
HOLIDAYS.add(LocalDate.of(2025, 1, 26)); // 春節前補班
HOLIDAYS.add(LocalDate.of(2025, 2, 8)); // 春節后補班
}
/**
* 判斷日期是否為工作日
*/
public static boolean isWeekday(LocalDate date) {
DayOfWeek dayOfWeek = date.getDayOfWeek();
return dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY;
}
/**
* 判斷日期是否為法定節假日
*/
public static boolean isHoliday(LocalDate date) {
return HOLIDAYS.contains(date);
}
/**
* 判斷日期是否為調休工作日
*/
public static boolean isAdjustedWorkday(LocalDate date) {
return ADJUSTED_WORKDAYS.contains(date);
}
/**
* 判斷日期是否為需要上班的日子(工作日或調休工作日)
*/
public static boolean isWorkingDay(LocalDate date) {
// 如果是法定節假日,不是工作日
if (isHoliday(date)) {
returnfalse;
}
// 如果是調休工作日,是工作日
if (isAdjustedWorkday(date)) {
returntrue;
}
// 否則根據周幾判斷
return isWeekday(date);
}
public static void main(String[] args) {
LocalDate date = LocalDate.of(2025, 2, 5); // 春節
System.out.println(date + " 是否為工作日: " + isWorkingDay(date));
date = LocalDate.of(2025, 1, 26); // 春節調休
System.out.println(date + " 是否為工作日: " + isWorkingDay(date));
date = LocalDate.of(2025, 7, 5); // 周日
System.out.println(date + " 是否為工作日: " + isWorkingDay(date));
}
}基于外部數據源的方案
基礎方案的局限性在于節假日數據需要硬編碼在代碼中,不利于維護和更新。更靈活的方案是將節假日數據存儲在外部文件或數據庫中:
public class HolidayUtil {
private static final String HOLIDAY_FILE = "holidays.txt";
private static final String ADJUSTED_WORKDAY_FILE = "adjusted_workdays.txt";
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private final Set<LocalDate> holidays = new HashSet<>();
private final Set<LocalDate> adjustedWorkdays = new HashSet<>();
public HolidayUtil() {
loadHolidays();
loadAdjustedWorkdays();
}
private void loadHolidays() {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(Objects.requireNonNull(
getClass().getClassLoader().getResourceAsStream(HOLIDAY_FILE))))) {
reader.lines()
.map(line -> line.trim())
.filter(line -> !line.isEmpty() && !line.startsWith("#"))
.map(dateStr -> LocalDate.parse(dateStr, DATE_FORMAT))
.forEach(holidays::add);
} catch (IOException e) {
System.err.println("加載節假日數據失敗: " + e.getMessage());
}
}
private void loadAdjustedWorkdays() {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(Objects.requireNonNull(
getClass().getClassLoader().getResourceAsStream(ADJUSTED_WORKDAY_FILE))))) {
reader.lines()
.map(line -> line.trim())
.filter(line -> !line.isEmpty() && !line.startsWith("#"))
.map(dateStr -> LocalDate.parse(dateStr, DATE_FORMAT))
.forEach(adjustedWorkdays::add);
} catch (IOException e) {
System.err.println("加載調休工作日數據失敗: " + e.getMessage());
}
}
/**
* 判斷日期是否為工作日
*/
public boolean isWeekday(LocalDate date) {
DayOfWeek dayOfWeek = date.getDayOfWeek();
return dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY;
}
/**
* 判斷日期是否為法定節假日
*/
public boolean isHoliday(LocalDate date) {
return holidays.contains(date);
}
/**
* 判斷日期是否為調休工作日
*/
public boolean isAdjustedWorkday(LocalDate date) {
return adjustedWorkdays.contains(date);
}
/**
* 判斷日期是否為需要上班的日子(工作日或調休工作日)
*/
public boolean isWorkingDay(LocalDate date) {
// 如果是法定節假日,不是工作日
if (isHoliday(date)) {
returnfalse;
}
// 如果是調休工作日,是工作日
if (isAdjustedWorkday(date)) {
returntrue;
}
// 否則根據周幾判斷
return isWeekday(date);
}
public static void main(String[] args) {
HolidayUtil util = new HolidayUtil();
LocalDate date = LocalDate.now();
System.out.println(date + " 是否為工作日: " + util.isWorkingDay(date));
}
}使用第三方 API 的方案
/**
* 基于timor.tech節假日API的工作日判斷工具
*/
public class TimorHolidayApiClient {
private static final String API_BASE_URL = "https://timor.tech/api/holiday/year/";
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter MONTH_DAY_FORMAT = DateTimeFormatter.ofPattern("MM-dd");
private final HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
private final ObjectMapper objectMapper = new ObjectMapper();
// 緩存每年的節假日數據,避免頻繁調用API
private final Map<Integer, HolidayResponse> holidayCache = new ConcurrentHashMap<>();
// 緩存日期到是否為工作日的映射,提升重復查詢性能
private final Map<LocalDate, Boolean> workdayCache = new ConcurrentHashMap<>();
/**
* 判斷指定日期是否為工作日
* @param date 要判斷的日期
* @returntrue表示工作日,false表示非工作日
*/
public boolean isWorkingDay(LocalDate date) {
// 先檢查緩存
if (workdayCache.containsKey(date)) {
return workdayCache.get(date);
}
try {
// 獲取該年份的節假日數據
HolidayResponse response = getHolidayData(date.getYear());
if (response == null || response.getHoliday().isEmpty()) {
// 沒有節假日數據時,按常規周末判斷
return isRegularWeekday(date);
}
// 格式化日期為"MM-dd"格式,用于API結果匹配
String monthDay = date.format(MONTH_DAY_FORMAT);
HolidayInfo holidayInfo = response.getHoliday().get(monthDay);
// 處理節假日情況
if (holidayInfo != null) {
// 如果是節假日,不是工作日
if (holidayInfo.isHoliday()) {
workdayCache.put(date, false);
returnfalse;
}
// 如果是補班日,是工作日
else {
workdayCache.put(date, true);
returntrue;
}
}
// 非節假日也非補班日,按常規周末判斷
else {
boolean isWeekday = isRegularWeekday(date);
workdayCache.put(date, isWeekday);
return isWeekday;
}
} catch (Exception e) {
System.err.println("判斷工作日失敗,日期: " + date + ", 錯誤: " + e.getMessage());
// 出錯時按常規工作日處理
boolean isWeekday = isRegularWeekday(date);
workdayCache.put(date, isWeekday);
return isWeekday;
}
}
/**
* 獲取指定年份的節假日數據,帶有緩存機制
*/
private HolidayResponse getHolidayData(int year) throws IOException, InterruptedException {
if (holidayCache.containsKey(year)) {
return holidayCache.get(year);
}
String url = API_BASE_URL + year;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Accept", "application/json")
.timeout(java.time.Duration.ofSeconds(10))
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
HolidayResponse holidayResponse = objectMapper.readValue(response.body(), HolidayResponse.class);
if (holidayResponse.getCode() == 0) { // API返回成功
holidayCache.put(year, holidayResponse);
return holidayResponse;
}
}
System.err.println("獲取節假日數據失敗,年份: " + year + ", 狀態碼: " + response.statusCode());
return new HolidayResponse(); // 返回空數據
}
/**
* 常規周末判斷(不考慮節假日和補班)
*/
private boolean isRegularWeekday(LocalDate date) {
DayOfWeek dayOfWeek = date.getDayOfWeek();
return dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY;
}
/**
* 獲取日期對應的節假日信息
*/
public HolidayInfo getHolidayInfo(LocalDate date) {
try {
HolidayResponse response = getHolidayData(date.getYear());
if (response == null || response.getHoliday().isEmpty()) {
return null;
}
String monthDay = date.format(MONTH_DAY_FORMAT);
return response.getHoliday().get(monthDay);
} catch (Exception e) {
System.err.println("獲取節假日信息失敗,日期: " + date + ", 錯誤: " + e.getMessage());
return null;
}
}
/**
* 清除緩存,適用于需要更新數據的場景
*/
public void clearCache() {
holidayCache.clear();
workdayCache.clear();
}
/**
* 示例用法
*/
public static void main(String[] args) {
TimorHolidayApiClient client = new TimorHolidayApiClient();
// 測試2025年部分日期
LocalDate[] dates = {
LocalDate.of(2025, 1, 1), // 元旦
LocalDate.of(2025, 1, 26), // 春節前補班
LocalDate.of(2025, 1, 28), // 除夕
LocalDate.of(2025, 2, 8), // 春節后補班
LocalDate.of(2025, 5, 1), // 勞動節
LocalDate.of(2025, 10, 1), // 國慶節
LocalDate.of(2025, 10, 11) // 國慶節后補班
};
for (LocalDate date : dates) {
boolean isWorkday = client.isWorkingDay(date);
HolidayInfo info = client.getHolidayInfo(date);
String status = isWorkday ? "工作日" : "非工作日";
String name = info != null ? info.getName() : "普通日";
System.out.printf("%s %s - %s%n",
date,
status,
name);
}
}
}責任編輯:龐桂玉
來源:
一安未來





















