工作日判断工具

1 背景

一些业务的处理是以工作日为周期进行处理,这就需要判断某个日期是否为工作日,以及计算n个工作日后的日期是多少。

2 工作日判断逻辑

默认情况下周六、日是假期,周一~周五是工作日。
但是国家法定假日和法定假日导致的周、六日调班会打破默认情况。
如果周六、日赶上调班也可以是工作日,周一~周五赶上法定假期也可以是假期

具体判断逻辑如【图1】:

工作日判断流程图

3实现方案

3.1 假期信息配置

如果要判断某个日期是否为工作日,首先要有每年的法定假日调班数据。
使用如下结构存储以上信息

{
    "2020": {
        "1": {
            "holidays": [
                1,
                24,
                25,
                26,
                27,
                28,
                29,
                30,
                31
            ],
            "shiftDays": [
                19
            ],
            "remark": "元旦"
        },
        "2": {
            "holidays": [],
            "shiftDays": [],
            "remark": "春节"
        },
        "4": {
            "holidays": [
                4,
                5,
                6
            ],
            "shiftDays": [
                26
            ],
            "remark": "清明、五一"
        },
        "5": {
            "holidays": [
                1,
                2,
                3,
                4,
                5
            ],
            "shiftDays": [
                9
            ],
            "remark": "五一"
        },
        "6": {
            "holidays": [
                25,
                26,
                27
            ],
            "shiftDays": [
                28
            ],
            "remark": "端午"
        },
        "9": {
            "holidays": [],
            "shiftDays": [
                27
            ],
            "remark": ""
        },
        "10": {
            "holidays": [
                1,
                2,
                3,
                4,
                5,
                6,
                7
            ],
            "shiftDays": [
                10
            ],
            "remark": "国庆"
        }
    }
}

3.2 Java结构

    /**
     * 假期配置
     * 从spring配置注入,从db读取配置,从apollo配置注入等
     */
    private static Map<Integer, Map<Integer, HolidayConfig>> holidayConfigs;
    
    /**
     * 每月的假期配置
     */
    @Data
    public static class HolidayConfig {
        // 假日
        private Set<Integer> holidays;
        // 调班
        private Set<Integer> shiftDays;
        // 备注
        private String remark;
    }
  • 第一层Integer为每年的假期配置
  • 第二层Integer为没月的假期配置
  • 每月的假期配置包含2个List,分别为法定假日和调班的dayOfMonth。

3.3 几个常用的工具方法

    /**
     * 判断date是否为工作日
     *
     * @param date
     * @return
     */
    public static boolean isWorkday(LocalDate date)

    /**
     * 得到在date上加n个工作日后的日期
     * 
     * @param date 偏移起始日期
     * @param n    在起始日期上加n个工作日(n可以为负数)
     * @return n个工作日后的日期
     * @throws Exception
     */
    public static LocalDate plusNWorkDay(LocalDate date, int n)

4 完整代码

package com.xuanfeng.tools.date;

import lombok.Data;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.Map;
import java.util.Set;

/**
 * @description: 工作日工具类
 * @author: xuanfeng
 * @create: 2020/08/21 16:15
 */

public class WorkDayUtil {

    /**
     * 假期配置
     * 从spring配置注入,从db读取配置,从apollo配置注入等
     */
    private static Map<Integer, Map<Integer, HolidayConfig>> holidayConfigs;

    /**
     * 得到在date上加n个工作日后的日期
     *
     * @param date 偏移起始日期
     * @param n    在起始日期上加n个工作日(n可以为负数)
     * @return n个工作日后的日期
     * @throws Exception
     */
    public static LocalDate plusNWorkDay(LocalDate date, int n) {
        // 绝对值
        int absN = Math.abs(n);

        // ±1,向前找或是向后找
        int sign = Integer.compare(n, 0);

        // 已发现的工作日数量
        int foundNum = 0;

        for (int i = 1; ; i++) {
            // 下一个工作日
            LocalDate nextDate = date.plusDays(i * sign);
            if (isWorkday(nextDate)) {

                // 当找到absN个工作日后返回
                if (++foundNum == absN) {
                    return nextDate;
                }
            }
        }
    }

    /**
     * 判断date是否为工作日
     *
     * @param date
     * @return
     */
    public static boolean isWorkday(LocalDate date) {
        HolidayConfig holidayConfig = null;
        // 如果有假期配置
        if (!CollectionUtils.isEmpty(holidayConfigs)) {
            // 取出年的配置
            Map<Integer, HolidayConfig> yearConfig = holidayConfigs.get(date.getYear());
            // 如果有年度配置
            if (!CollectionUtils.isEmpty(yearConfig)) {
                // 取出月的配置
                holidayConfig = yearConfig.get(date.getMonthValue());
            }
        }

        DayOfWeek dayOfWeek = date.getDayOfWeek();
        Integer dayOfMonth = date.getDayOfMonth();

        // 如果没有本月的假期配置(只判断周六日)
        if (holidayConfig == null) {
            if (dayOfWeek == DayOfWeek.SUNDAY || dayOfWeek == DayOfWeek.SATURDAY) {
                return false;
            } else {
                return true;
            }
        }
        // 有本月假期配置
        else {
            // 周六日
            if (dayOfWeek == DayOfWeek.SUNDAY || dayOfWeek == DayOfWeek.SATURDAY) {
                //周六周日需要判断是否调班
                Set<Integer> shiftDays = holidayConfig.getShiftDays();
                if (CollectionUtils.isEmpty((shiftDays))) {
                    return false;
                } else {
                    return shiftDays.contains(dayOfMonth);
                }

            }
            // 非周六日
            else {
                // 判断是否在假期内
                Set<Integer> holidays = holidayConfig.getHolidays();
                if (CollectionUtils.isEmpty(holidays)) {
                    return true;
                } else {
                    return !holidays.contains(dayOfMonth);
                }
            }
        }
    }

    /**
     * 每月的假期配置
     */
    @Data
    public static class HolidayConfig {
        // 假日
        private Set<Integer> holidays;
        // 调班
        private Set<Integer> shiftDays;
        // 备注
        private String remark;
    }

    public static void setHolidayConfigs(Map<Integer, Map<Integer, HolidayConfig>> holidayConfigs) {
        WorkDayUtil.holidayConfigs = holidayConfigs;
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值