我们在一些特殊的业务场景下,想要获取到下一个工作日,这里的工作日指正常的法定工作日(包含调休日),这个需求来源于银联的提现,银联只能在法定工作日才能体现,那么在业务代码里对提现日期必须就是工作日即可,在查询了相关的信息,目前没有稳定的API或者三方付费的稳定API,那么这个需求其实不难处理,自己也可以动手维护一套WorkingHolidayAPI的,那么我就以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', '中秋节');
调用IWorkingHolidayService.nextWorkingDay(curDate) 即可
实现过程也很简单,因为法定节假日法定工作日长时间是稳定不变,在此基础上,我们先通过以数据表为基础,再以hutool为辅助,来完成近365天的所有法定日的标记,通过维护好的线程安全map我们很快就能获取到具体某一日的下一个工作日是哪一天。
很简单的实现不是么,没有花里胡哨的操作,一起加油