获取每月最后一个工作日:考虑法定节假日与调休

在许多业务场景中,了解每个月的最后一个工作日对于财务结算、报告生成等至关重要。然而,确定这一日期时必须考虑到国家的法定节假日以及可能存在的调休安排。本文将介绍如何通过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);
        }
    }
实现这个功能需要以下几个步骤: 1. 获取法定节假日列表和调休日列表,这些数据可以从后端接口或者本地存储中获取。 2. 计算起止日期之间的所有日期,并将它们存储在一个数组中。 3. 遍历数组,对于每个日期,判断它是否是周末或者法定节假日或者调休日。如果是,排除它。 4. 最后计算剩余的日期数量,即为工作日的天数。 下面是一个使用 JavaScript 实现的示例代码: ```javascript // 假设法定节假日调休日的数据分别存储在 holidayList 和 adjustedList 中 function getWorkingDays(startDate, endDate, holidayList, adjustedList) { // 计算起止日期之间的所有日期 const dateList = []; let currentDate = new Date(startDate); while (currentDate <= endDate) { dateList.push(new Date(currentDate)); currentDate.setDate(currentDate.getDate() + 1); } // 排除周末和法定节假日调休日 let workingDays = 0; dateList.forEach(date => { // 判断是否是周末 if (date.getDay() !== 0 && date.getDay() !== 6) { // 判断是否是法定节假日 const isHoliday = holidayList.some(holiday => { return holiday === date.toISOString().slice(0, 10); }); // 判断是否是调休日 const isAdjust = adjustedList.some(adjust => { return adjust === date.toISOString().slice(0, 10); }); if (!isHoliday || isAdjust) { workingDays++; } } }); return workingDays; } // 调用示例 const holidayList = ["2022-01-01", "2022-01-02", "2022-01-03"]; // 假设法定节假日为2022年元旦 const adjustedList = ["2022-01-08"]; // 假设2022年元旦调休到了2022年1月8日 const startDate = new Date("2022-01-01"); const endDate = new Date("2022-01-10"); const workingDays = getWorkingDays(startDate, endDate, holidayList, adjustedList); console.log(workingDays); // 输出结果为 3,即2022年1月4日、1月5日、1月7日为工作日 ``` 这个示例代码比较简单,实际开发中还需要考虑一些边界情况和优化性能的问题,例如如何提前加载节假日调休日数据,如何处理时区等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿熊跃晖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值