JAVA节假日工具类服务分享(需联网)
在 Java 开发中,处理节假日相关逻辑几乎是企业级应用的 “标配需求”—— 无论是计算订单生效周期、生成自动提醒日程,还是统计工作日数据,都离不开准确的节假日与调休信息支撑。但用过 Java 原生java.time包的开发者都清楚,标准库只提供了日期时间的基础处理能力,完全不包含节假日这类具有地域特性且动态变化的数据。
正是踩过这些坑后,我们最终确定了基于联网 API 的节假日工具类方案。通过对接可靠的公共节假日 API,既能直接获取权威的法定节假日名称、日期,还能精准识别调休工作日(如周末需上班的情况),甚至支持按年、按月、按日灵活查询。这种方式彻底解决了数据时效性问题,同时通过封装 HTTP 请求、JSON 解析和结果缓存逻辑,让业务层能以极简的方式调用。
接下来分享的工具类,就是基于这一思路实现的核心代码,包含了 API 对接、响应解析、异常处理等关键逻辑,可直接集成到 Spring Boot 等项目中使用。
节假日实体定义
package test;
import cn.hutool.core.date.DateUtil;
import java.util.Date;
/**
* 节假日数据.
*
* @author wangbing
* @version 0.0.1
* @since 1.8
*/
public class HolidayData {
/**
* 工作日
*/
public static final int WEEKDAY = 0;
/**
* 周末
*/
public static final int WEEKEND = 1;
/**
* 节日
*/
public static final int HOLIDAY = 2;
/**
* 调休(补班)
*/
public static final int EXTRA_DAY = 3;
public HolidayData() {
this(DateUtil.date());
}
public HolidayData(String date) {
this(DateUtil.parseDate(date));
}
public HolidayData(Date date) {
this.type = DateUtil.isWeekend(date) ? WEEKEND : WEEKDAY;
this.name = DateUtil.isWeekend(date) ? "周末" : "工作日";
this.date = DateUtil.formatDate(date);
this.holiday = false;
this.workday = !DateUtil.isWeekend(date);
this.weekend = DateUtil.isWeekend(date);
}
/**
* 类型
*/
private int type;
/**
* 说明
*/
private String name;
/**
* 日期yyyy-MM-dd
*/
private String date;
/**
* 是否节假日
*/
private boolean holiday;
/**
* 是否工作日
*/
private boolean workday;
/**
* 是否周末
*/
private boolean weekend;
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public boolean isHoliday() {
return holiday;
}
public void setHoliday(boolean holiday) {
this.holiday = holiday;
}
public boolean isWorkday() {
return workday;
}
public void setWorkday(boolean workday) {
this.workday = workday;
}
public boolean isWeekend() {
return weekend;
}
public void setWeekend(boolean weekend) {
this.weekend = weekend;
}
}
实现逻辑
package xyz.wbsite.jtask;
import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import xyz.wbsite.jmacro.util.DateUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* 节假日服务提供者
*
* @author wangbing
* @version 0.0.1
* @since 1.8
*/
public class HolidayProvider {
/**
* 添加Hutool定时缓存,缓存有效期设为24小时
*/
private final TimedCache<String, List<HolidayData>> holidayCache = CacheUtil.newTimedCache(24 * 60 * 60 * 1000);
public List<HolidayData> getHolidayByYear(String year) {
// 先从缓存获取
List<HolidayData> cachedData = holidayCache.get(year);
if (cachedData != null) {
return cachedData;
}
// 缓存未命中,调用原方法获取数据
List<HolidayData> freshData = pullFormTimor(year);
// 存入缓存
if (!freshData.isEmpty()) {
holidayCache.put(year, freshData);
}
return freshData;
}
public List<HolidayData> getHolidayByMonth(String month) {
List<HolidayData> result = new ArrayList<>();
// 直接使用当前实例,移除Spring的LocalData.getBean调用
List<HolidayData> holidayByYear = getHolidayByYear(month.substring(0, 4));
for (HolidayData holidayData : holidayByYear) {
if (holidayData.getDate().startsWith(month)) {
result.add(holidayData);
}
}
return result;
}
public HolidayData getHolidayByDate(String date) {
// 直接使用当前实例,移除Spring的LocalData.getBean调用
List<HolidayData> holidayByYear = getHolidayByYear(date.substring(0, 4));
for (HolidayData holidayData : holidayByYear) {
if (holidayData.getDate().equals(date)) {
return holidayData;
}
}
return new HolidayData(date);
}
/**
* 是否节假日
*
* @param date 日期
* @return 是否节假日
*/
public boolean isHoliday(String date) {
return getHolidayByDate(date).isHoliday();
}
/**
* 是否工作日
*
* @param date 日期
* @return 是否工作日
*/
public boolean isWorkday(String date) {
return getHolidayByDate(date).isWorkday();
}
/**
* 是否周末
*
* @param date 日期
* @return 是否周末
*/
public boolean isWeekend(String date) {
return getHolidayByDate(date).isWeekend();
}
/**
* 提莫的神秘小站
*/
private List<HolidayData> pullFormTimor(String year) {
List<HolidayData> result = new ArrayList<>();
try {
String body = HttpUtil.get(StrUtil.format("http://timor.tech/api/holiday/year/{}", year));
// {"code":"0","holiday":{"01-01":{"holiday":true,"name":"元旦","wage":3,"date":"2023-01-01"}...}}
JSONObject entries = JSONUtil.parseObj(body);
String rspCode = "code";
if (entries.getInt(rspCode, -1) != 0) {
return Collections.emptyList();
}
JSONObject holiday = entries.getJSONObject("holiday");
for (Map.Entry<String, Object> entry : holiday) {
JSONObject ho = (JSONObject) entry.getValue();
HolidayData holidayData = new HolidayData();
holidayData.setDate(ho.getStr("date"));
holidayData.setName(ho.getStr("name"));
// 判断是否为法定节假日
holidayData.setHoliday(ho.getBool("holiday"));
// 非法定节假日为工作日
holidayData.setWorkday(!holidayData.isHoliday());
// 是否为周末(周六周日),可以直接判断
holidayData.setWeekend(DateUtil.isWeekend(DateUtil.parse(holidayData.getDate())));
// 可以直接获取,但为了后期扩展,这里根据是否为法定节假日判断类型
// 提莫的神秘小站返回的数据只有两种类型:法定节假日和调休(补班)
holidayData.setType(holidayData.isHoliday() ? HolidayData.HOLIDAY : HolidayData.EXTRA_DAY);
result.add(holidayData);
}
} catch (Exception e) {
System.err.println("获取节假日数据失败: " + e.getMessage());
return Collections.emptyList();
}
return result;
}
}
以上代码直接引入即可使用,其中为了防止频繁调用api接口使用了hutool的缓存工具,如果不使用也是可以的,只需要将相关缓存代码删除即可。