获取下一个工作日/休息日的方法 本地基础搭建 Springboot 法定节假日/休息日/调休日 均可 不用调三方

场景

我们在一些特殊的业务场景下,想要获取到下一个工作日,这里的工作日指正常的法定工作日(包含调休日),这个需求来源于银联的提现,银联只能在法定工作日才能体现,那么在业务代码里对提现日期必须就是工作日即可,在查询了相关的信息,目前没有稳定的API或者三方付费的稳定API,那么这个需求其实不难处理,自己也可以动手维护一套WorkingHolidayAPI的,那么我就以Springboot简答举例写出重要的环节。


文章目录

  • 场景
  • 代码
  • Springboot单元测试
  • 原理
    • 优化项
  • 总结


代码

关键工具:


import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.Week;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class WorkingHolidayUtil {

    private static final int DEFAULT_YEAR_DAY = 365;

    private Map<Integer, WorkingHoliday> whMap;
    /**
     * 创建时间
     */
    private Date createTime;
    /**
     * 起始日期
     */
    private Date startDay;
    /**
     *
     */
    private int offDay;
    /**
     * 权重比例
     *
     */
    private int[] WEIGHT_ARR = {10000, 100, 1};

    public WorkingHolidayUtil(List<WorkingHolidayEntity> data, Date startDay, int offDay) {
        if (data.isEmpty()) {
            throw new RuntimeException("构建参数源不能为空");
        }
        if (startDay == null) {
            throw new RuntimeException("构建参数源起始日期不能为空");
        }
        if (offDay <= 0) {
            throw new RuntimeException("构建参数偏移天数只能为正整数");
        }
        this.createTime = new Date();
        this.startDay = startDay;
        this.offDay = offDay > DEFAULT_YEAR_DAY * 10 ? DEFAULT_YEAR_DAY * 10 : offDay;
        this.whMap = new ConcurrentHashMap<>(offDay);
        buildBaseNode(data);
    }

    public WorkingHolidayUtil(List<WorkingHolidayEntity> data, Date startDay) {
        if (data.isEmpty()) {
            throw new RuntimeException("构建参数源不能为空");
        }
        if (startDay == null) {
            throw new RuntimeException("构建参数源起始日期不能为空");
        }
        this.createTime = new Date();
        this.startDay = startDay;
        this.offDay = DEFAULT_YEAR_DAY;
        this.whMap = new ConcurrentHashMap<>(offDay);
        buildBaseNode(data);
    }

    private boolean buildBaseNode(List<WorkingHolidayEntity> data) {
        Map<String, WorkingHolidayEntity> dataMap = new HashMap<>(1 << 10);
        if (!data.isEmpty()) {
            for (WorkingHolidayEntity whe : data) {
                dataMap.put(whe.getDayStr(), whe);
            }
        }
        return doBuildBaseNode(dataMap);
    }


    private boolean doBuildBaseNode(Map<String, WorkingHolidayEntity> dataMap) {

        Date curDate = DateUtil.date(startDay);
        String date = "unknow";
        int weight = -1;
        String source = "unknow";
        int holidayOrWorkingday = -1;
        WorkingHoliday lastWorkingDay = null;
        List<WorkingHoliday> lastWorkingDays = new ArrayList<>(DEFAULT_YEAR_DAY);
        WorkingHoliday lastHolidayDay = null;
        List<WorkingHoliday> lastHolidayDays = new ArrayList<>(DEFAULT_YEAR_DAY);
        String remark = "";
        for (int i = 0; i < offDay; i++) {
            date = DateUtil.format(curDate, "yyyy-MM-dd");
            weight = getWeight(curDate);
            if (!dataMap.isEmpty() && dataMap.containsKey(date)) {
                WorkingHolidayEntity dbItem = dataMap.get(date);
                source = "db";
                holidayOrWorkingday = dbItem.getHolidayOrWorkingday();
                remark = dbItem.getRemark();
            } else {

                Week week = DateUtil.dayOfWeekEnum(curDate);
                int dayOfWeek = week.getValue();
                source = "hutool";
                if (dayOfWeek == 1 || dayOfWeek == 7) {
                    holidayOrWorkingday = 2;
                } else {
                    holidayOrWorkingday = 3;
                }
                remark = week.toChinese();
            }
            WorkingHoliday wh = new WorkingHoliday(weight, date, holidayOrWorkingday, source, remark);

            // 偶休息 奇工作
            if (holidayOrWorkingday % 2 == 0) {
                clearArrs(0, lastWorkingDays, weight);
                if (lastHolidayDay != null) {
                    lastHolidayDay.setNextHolidayDayWeight(weight);
                }
                lastHolidayDay = wh;
                lastHolidayDays.add(lastHolidayDay);
            } else {
                clearArrs(1, lastHolidayDays, weight);
                if (lastWorkingDay != null) {
                    lastWorkingDay.setNextWorkingDayWeight(weight);
                }
                lastWorkingDay = wh;
                lastWorkingDays.add(lastWorkingDay);
            }
            whMap.put(weight, wh);
            curDate = DateUtil.offsetDay(curDate, 1);
        }

        return !whMap.isEmpty();
    }

    /**
     * @param holidayOrWorkingday
     * @param workingHolidays
     * @param weight
     */
    private void clearArrs(int holidayOrWorkingday, List<WorkingHoliday> workingHolidays, int weight) {
        if (holidayOrWorkingday == 0) {
            for (WorkingHoliday e : workingHolidays) {
                e.setNextHolidayDayWeight(weight);
            }
        } else {
            for (WorkingHoliday e : workingHolidays) {
                e.setNextWorkingDayWeight(weight);
            }
        }
        workingHolidays.clear();
    }

    private int getWeight(Date curDate) {
        int year = DateUtil.year(curDate);
        int month = DateUtil.month(curDate) + 1;
        int day = DateUtil.dayOfMonth(curDate);
        return WEIGHT_ARR[0] * year + WEIGHT_ARR[1] * month + WEIGHT_ARR[2] * day;

    }

    private int getWeight(String curDate) {
        return getWeight(DateUtil.parse(curDate, "yyyy-MM-dd"));
    }

    public WorkingHoliday nextWorkingDay(String curDate) {
        int weight = getWeight(curDate);
        if (!whMap.containsKey(weight)) {
            throw new RuntimeException("数据源不存在当前日期");
        }
        WorkingHoliday workingHoliday = whMap.get(weight);
        while (true) {
            if (workingHoliday == null) {
                throw new RuntimeException("目前找不到下一个工作日");
            }
            Integer nextWorkingDayWeight = workingHoliday.getNextWorkingDayWeight();
            if (nextWorkingDayWeight != null) {
                return whMap.get(nextWorkingDayWeight);
            } else {
                String date = workingHoliday.getDate();
                DateTime dateTime = DateUtil.offsetDay(DateUtil.parse(date, "yyyy-MM-dd"), 1);
                int nextDayWeight = getWeight(dateTime);
                workingHoliday = whMap.containsKey(nextDayWeight) ? whMap.get(nextDayWeight) : null;
            }
        }
    }


}



模型:

public class WorkingHoliday {
    /**
     * 权重
     */
    private int weight;
    /**
     * 日期
     * yyyy-MM-dd
     */
    private String date;
    /**
     * 法定工作日/休息日
     * 0法定休息日
     * 1法定工作日
     * 2正常休息日
     * 3正常工作日
     * 

* 偶数休息 奇数工作 */ private int holidayOrWorkingday; /** * 来源 * db数据库 hutool糊涂 */ private String source; /** * 下一个工作日权重 */ private Integer nextWorkingDayWeight; /** * 下一个休息日权重 */ private Integer nextHolidayDayWeight; private String remark; public WorkingHoliday() { } public WorkingHoliday(int weight, String date, int holidayOrWorkingday, String source, String remark) { this.weight = weight; this.date = date; this.holidayOrWorkingday = holidayOrWorkingday; this.source = source; this.remark = remark; } public Integer getNextWorkingDayWeight() { return nextWorkingDayWeight; } public void setNextWorkingDayWeight(Integer nextWorkingDayWeight) { this.nextWorkingDayWeight = nextWorkingDayWeight; } public Integer getNextHolidayDayWeight() { return nextHolidayDayWeight; } public void setNextHolidayDayWeight(Integer nextHolidayDayWeight) { this.nextHolidayDayWeight = nextHolidayDayWeight; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } public String getDate() { return date; } public void setDate(String date) { this.date = date; } public int getHolidayOrWorkingday() { return holidayOrWorkingday; } public void setHolidayOrWorkingday(int holidayOrWorkingday) { this.holidayOrWorkingday = holidayOrWorkingday; } public String getSource() { return source; } public void setSource(String source) { this.source = source; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } @Override public String toString() { return "WorkingHoliday{" + "weight=" + weight + ", date='" + date + '\'' + ", holidayOrWorkingday=" + holidayOrWorkingday + ", source='" + source + '\'' + ", nextWorkingDayWeight=" + nextWorkingDayWeight + ", nextHolidayDayWeight=" + nextHolidayDayWeight + ", remark='" + remark + '\'' + '}'; } }

实体类

package com..finance.common.entity;

import java.io.Serializable;
import java.util.Date;

public class WorkingHolidayEntity  implements Serializable {
    /**
     * 日期
     */
    private Date dayStr;
    /**
     * 法定工作日/休息日
     * 0法定休息日
     * 1法定工作日
     * 2正常休息日
     * 3正常工作日
     *
     * 偶数休息 奇数工作
     */
    private int holidayOrWorkingday;
    /**
     * 备注
     */
    private String remark;

    public Date getDayStr() {
        return dayStr;
    }

    public void setDayStr(Date dayStr) {
        this.dayStr = dayStr;
    }

    public int getHolidayOrWorkingday() {
        return holidayOrWorkingday;
    }

    public void setHolidayOrWorkingday(int holidayOrWorkingday) {
        this.holidayOrWorkingday = holidayOrWorkingday;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }

    @Override
    public String toString() {
        return "WorkingHolidayEntity{" +
                "dayStr='" + dayStr + '\'' +
                ", holidayOrWorkingday=" + holidayOrWorkingday +
                ", remark='" + remark + '\'' +
                '}';
    }
}

业务实现类

@Service
public class WorkingHolidayServiceImpl implements IWorkingHolidayService {

    private WorkingHolidayUtil workingHolidayUtil;

    private String glCurDate;

    @Autowired
    private WorkingHolidayDao workingHolidayDao;

    @Override
    public WorkingHoliday nextWorkingDay(String curDate) {
        if (!curDate.equals(glCurDate) || workingHolidayUtil == null) {
            synchronized (this) {
                if (!curDate.equals(glCurDate) || workingHolidayUtil == null) {
                    DateTime dateTime = DateUtil.parse(curDate, "yyyy-MM-dd");
                    List<WorkingHolidayEntity> datas = workingHolidayDao.egYearWorkingHolidays(String.valueOf(dateTime.year()));
                    workingHolidayUtil = new WorkingHolidayUtil(datas, dateTime);
                    glCurDate = curDate;
                }
            }
        }
        return workingHolidayUtil.nextWorkingDay(curDate);
    }


}

Dao关键SQL语句

<select id="egYearWorkingHolidays" parameterClass="java.lang.String"
            resultClass="com..finance.common.entity.WorkingHolidayEntity">
        SELECT
            DAY_STR dayStr,
            HOLIDAY_OR_WORKINGDAY  holidayOrWorkingday,
            REMARK remark
        FROM
            "WORKING_HOLIDAY"
        WHERE
            SUBSTR( DAY_STR, 0, 4 ) <![CDATA[>= ]]> #year#
        ORDER BY
            DAY_STR ASC
    </select>

DDL表构建(oracle)

CREATE TABLE "WORKING_HOLIDAY" 
   (	"DAY_STR" VARCHAR2(10) NOT NULL ENABLE, 
	"HOLIDAY_OR_WORKINGDAY" CHAR(1) NOT NULL ENABLE, 
	"REMARK" VARCHAR2(500)
   )

表数据填充

INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-01-01', '0', '元旦');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-01-02', '0', '元旦');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-01-03', '0', '元旦');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-01-29', '1', '春节调休');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-01-30', '1', '春节调休');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-01-31', '0', '春节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-02-01', '0', '春节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-02-02', '0', '春节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-02-03', '0', '春节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-02-04', '0', '春节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-02-05', '0', '春节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-02-06', '0', '春节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-04-02', '1', '清明节调休');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-04-03', '0', '清明节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-04-04', '0', '清明节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-04-05', '0', '清明节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-04-24', '1', '劳动节调休');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-04-30', '0', '劳动节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-05-01', '0', '劳动节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-05-02', '0', '劳动节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-05-03', '0', '劳动节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-05-04', '0', '劳动节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-05-07', '1', '劳动节调休');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-06-03', '0', '端午节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-06-04', '0', '端午节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-06-05', '0', '端午节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-09-10', '0', '中秋节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-09-11', '0', '中秋节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-09-12', '0', '中秋节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-10-01', '0', '国庆节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-10-02', '0', '国庆节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-10-03', '0', '国庆节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-10-04', '0', '国庆节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-10-05', '0', '国庆节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-10-06', '0', '国庆节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-10-07', '0', '国庆节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-10-08', '1', '国庆节调休');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2022-10-09', '1', '国庆节调休');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-09-21', '0', '中秋节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-09-18', '1', '中秋节调休');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-10-01', '0', '国庆节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-10-02', '0', '国庆节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-10-03', '0', '国庆节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-10-04', '0', '国庆节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-10-05', '0', '国庆节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-10-06', '0', '国庆节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-10-07', '0', '国庆节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-10-09', '1', '国庆节调休');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-09-26', '1', '国庆节调休');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-01-01', '0', '元旦');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-01-02', '0', '元旦');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-01-03', '0', '元旦');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-02-11', '0', '春节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-02-12', '0', '春节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-02-13', '0', '春节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-02-14', '0', '春节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-02-15', '0', '春节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-02-16', '0', '春节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-02-17', '0', '春节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-02-07', '1', '春节调休');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-02-20', '1', '春节调休');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-04-03', '0', '清明节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-04-04', '0', '清明节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-04-05', '0', '清明节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-05-01', '0', '劳动节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-05-02', '0', '劳动节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-05-03', '0', '劳动节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-05-04', '0', '劳动节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-05-05', '0', '劳动节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-04-25', '1', '劳动节调休');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-05-08', '1', '劳动节调休');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-06-12', '0', '端午节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-06-13', '0', '端午节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-06-14', '0', '端午节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-09-19', '0', '中秋节');
INSERT INTO "WORKING_HOLIDAY" VALUES ('2021-09-20', '0', '中秋节');

Springboot单元测试

调用IWorkingHolidayService.nextWorkingDay(curDate) 即可

原理

实现过程也很简单,因为法定节假日法定工作日长时间是稳定不变,在此基础上,我们先通过以数据表为基础,再以hutool为辅助,来完成近365天的所有法定日的标记,通过维护好的线程安全map我们很快就能获取到具体某一日的下一个工作日是哪一天。

优化项

  1. 可以将实时获取到数据库数据的操作改为每天一获取最新的
  2. 根据大部分业务来说固定offDay的长度,并注入到容器中
  3. 完善其方法,比如下一个休息日等需要的方法

总结

很简单的实现不是么,没有花里胡哨的操作,一起加油

你可能感兴趣的:(基础,spring,boot,java,后端)