在许多业务场景中,了解每个月的最后一个工作日对于财务结算、报告生成等至关重要。然而,确定这一日期时必须考虑到国家的法定节假日以及可能存在的调休安排。本文将介绍如何通过Java编写一个工具类来获取指定月份的最后一个工作日,并利用第三方免费API来处理法定节假日和调休情况。
一、准备工作
首先,你需要引入一些必要的依赖库,如Hutool
用于日期操作和JSON解析,Fastjson
用于简化JSON处理等。如果你使用的是Maven项目,请确保你的pom.xml
文件中包含以下依赖:
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.11</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
</dependencies>
二、实现逻辑
1. 获取节假日数据
我们将从第三方API http://timor.tech/api/holiday/year/{year}
获取指定年份的所有节假日信息。这个API返回的数据包含了所有节假日及其是否为调休的信息。
private static Map<String, Boolean> fetchHolidays(int year) {
Map<String, Boolean> holidayMap = new HashMap<>();
String s = HttpUtils.sendGet("http://timor.tech/api/holiday/year/" + year);
JSONObject jsonObject = JSONUtil.parseObj(s);
JSONObject holidayMapJson = jsonObject.getJSONObject("holiday");
if (Objects.isNull(holidayMapJson)) {
return null;
}
// true表示是节假日,false表示是调休
holidayMapJson.forEach((k, v) -> {
JSONObject dayInfoObj = (JSONObject) v;
holidayMap.put((String) dayInfoObj.get("date"), (Boolean) dayInfoObj.get("holiday"));
});
return holidayMap;
}
2. 计算最后的工作日
接下来,我们定义一个方法来计算给定月份的最后一个工作日,同时考虑法定节假日和调休。
public static Date getLastWorkingDayOfMonth(Map<String, Boolean> specialDays) {
Date lastDayOfMonth = DateUtil.endOfMonth(new Date());
Calendar calendar = DateUtil.calendar(lastDayOfMonth);
while (!isWorkingDay(calendar, specialDays)) {
calendar.add(Calendar.DAY_OF_MONTH, -1);
}
return calendar.getTime();
}
3. 判断某天是否为工作日
最后,我们需要一个辅助函数来判断某一天是否为工作日(排除周末和节假日)。
private static boolean isWorkingDay(Calendar calendar) {
Map<String, Boolean> holidayMap = this.buildMapByYear(LocalDate.now().getYear());
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
String dateStr = DateUtil.format(calendar.getTime(), "yyyy-MM-dd");
// 默认情况下,周六、周日是非工作日
if ((dayOfWeek == Calendar.SATURDAY || dayOfWeek == Calendar.SUNDAY)) {
// 如果这一天被特别指定为工作日,则视为工作日
return !holidayMap.getOrDefault(dateStr, true);
}
// 检查是否为法定假日或不在特殊安排中的普通周末
return !(holidayMap.containsKey(dateStr) && holidayMap.get(dateStr));
}
三、测试与运行
现在,我们可以编写一个简单的主函数来测试我们的功能。
public static void main(String[] args) {
System.out.println("本月的最后一个工作日为:" + DateUtil.format(getLastWorkingDayOfMonth(fetchHolidays(DateUtil.year(new Date()))), "yyyy-MM-dd"));
}
四、总结
通过上述步骤,我们成功地创建了一个工具类,可以方便地获取每个月的最后一个工作日,并充分考虑了法定节假日和调休的影响。这种方法不仅提高了准确性,还能灵活应对不同的业务需求变化。
全部源码,调整如下:
@Component
public class LastWorkingDayUtil {
@Autowired
private RedisUtil redisUtil;
/**
* 计算本月的最后一个工作日,考虑到法定节假日和调休
*/
public Date getLastWorkingDayOfMonth() {
Date lastDayOfMonth = DateUtil.endOfMonth(new Date());
Calendar calendar = DateUtil.calendar(lastDayOfMonth);
while (!isWorkingDay(calendar)) {
calendar.add(Calendar.DAY_OF_MONTH, -1);
}
return calendar.getTime();
}
public Date getLastWorkingDayOfMonth(Date date) {
Date lastDayOfMonth = DateUtil.endOfMonth(date);
Calendar calendar = DateUtil.calendar(lastDayOfMonth);
while (!isWorkingDay(calendar)) {
calendar.add(Calendar.DAY_OF_MONTH, -1);
}
return calendar.getTime();
}
/**
* 判断给定日期是否为工作日,考虑法定节假日和调休
*/
private boolean isWorkingDay(Calendar calendar) {
Map<String, Boolean> holidayMap = this.buildMapByYear(LocalDate.now().getYear());
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
String dateStr = DateUtil.format(calendar.getTime(), "yyyy-MM-dd");
// 默认情况下,周六、周日是非工作日
if ((dayOfWeek == Calendar.SATURDAY || dayOfWeek == Calendar.SUNDAY)) {
// 如果这一天被特别指定为工作日,则视为工作日
return !holidayMap.getOrDefault(dateStr, true);
}
// 检查是否为法定假日或不在特殊安排中的普通周末
return !(holidayMap.containsKey(dateStr) && holidayMap.get(dateStr));
}
private Map<String, Boolean> buildMapByYear(int year) {
Map<String, Boolean> holidayMap = new HashMap<>();
Object object = redisUtil.get("HOLIDAY_YEAR_" + year);
String s = null;
if(ObjectUtil.isEmpty(object)) {
s = HttpUtils.sendGet("http://timor.tech/api/holiday/year/" + year);
redisUtil.set("HOLIDAY_YEAR_" + year, s, 60 * 60 * 24 * 30);
}else{
s = object.toString();
}
JSONObject jsonObject = JSONObject.parseObject(s);
JSONObject holidayMapJson = null;
if (jsonObject != null) {
holidayMapJson = jsonObject.getJSONObject("holiday");
}
if (Objects.isNull(holidayMapJson)) {
return null;
}
holidayMapJson.forEach((k, v) -> {
JSONObject dayInfoObj = (JSONObject) v;
holidayMap.put(dayInfoObj.getString("date"), dayInfoObj.getBoolean("holiday"));
});
return holidayMap;
}
}
测试代码:
@Test
public void testLastDay(){
DateTime dateTime = DateUtil.parseDate("2025-01-01");
for (int i = 0; i < 12; i++) {
String lastWorkingDayOfMonth = DateUtil.formatDate(lastWorkingDayUtil.getLastWorkingDayOfMonth(DateUtil.offsetMonth(dateTime,i)));
System.out.println(lastWorkingDayOfMonth);
}
}