在开发中,可能会遇到这种需求:T+n日执行某些操作。n就是延迟n天。
例:用户于2019-10-01
日发起交易,系统会在T日进行受理,那么这个受理实际上是2019-10-08
号才会真正去处理用户的请求。
实际上上面的例子就是T+0
进行处理。
实际上我们在开发这个工具类时,要明确一下这几点:
- 非工作日包含
周末
+法定节假日
,而法定节假日是不固定的,即每年都会改变的,法定节假日还会有周末调休的工作日,所以需要DB或properties配置; - 用户上送当前日期,我们需要判断当前日期是否是工作日,若不是则选取一个最近的工作日;
- 使用
Calendar
类,我们可以计算今天是否是周末;
转换工具类:
public class WorkDayCalculateUtils {
/**
* 法定节假日-周末调休上班
*/
private static final List<String> SPECIAL_WORK_DAYS = new ArrayList<>(16);
/**
* 法定节假日
*/
private static final List<String> SPECIAL_REST_DAYS = new ArrayList<>(16);
//只读属性的
private static List<Integer> REST_DAYS = new ArrayList<>(2);
//项目启动后,加载配置文件的节假日日期
static {
REST_DAYS.add(Calendar.SATURDAY);
REST_DAYS.add(Calendar.SUNDAY);
REST_DAYS = Collections.unmodifiableList(REST_DAYS);
initSpecialDays();
}
/**
* 将当前日期转换为最近的工作日期
*
* @param currentDate
* @return
*/
public static Date getWorkDate(Date currentDate) {
Calendar calendarDate = Calendar.getInstance();
calendarDate.setTime(currentDate);
while (true) {
//节假日的情况
if (REST_DAYS.contains(calendarDate.get(Calendar.DAY_OF_WEEK))) {
//节假日调休的情况
if (SPECIAL_WORK_DAYS.contains(DateUtil.formatDate2Str(calendarDate.getTime(), "yyyy-MM-dd"))) {
return calendarDate.getTime();
}
//日期+1
} else {
//工作日
if (!SPECIAL_REST_DAYS.contains(DateUtil.formatDate2Str(calendarDate.getTime(), "yyyy-MM-dd"))) {
return calendarDate.getTime();
}
}
calendarDate.add(Calendar.DATE, 1);
}
}
/**
* @param currentDate 当前时间
* @param delayDays 延迟的天数(可以取正数或0)
* @return 距离当前时间延迟天数最近的工作日
*/
public static Date getWorkDate(Date currentDate, int delayDays) {
//1. 当前日期转换为Calendar时间
Calendar calendarDate = Calendar.getInstance();
calendarDate.setTime(currentDate);
//若是currentDate不是工作日,那么实际上要是最近的工作日才可提现
if (delayDays == 0) {
return getWorkDate(currentDate);
}
int i = 0;
/**
*
* 满足条件跳出循环后,返回的calendarDate是加+1后的值,但calendarDate可能是工作日,
* 也可能是给工作日,还是要进行一次计算。
*/
while (i < delayDays) {
//如果说当前时间周末
if (REST_DAYS.contains(calendarDate.get(Calendar.DAY_OF_WEEK))) {
//但是[法定节假日-调休上班]的情况下,也算是工作日
if (SPECIAL_WORK_DAYS.contains(DateUtil.formatDate2Str(calendarDate.getTime(), "yyyy-MM-dd"))) {
i++;
}
} else {
//如果当前时间是[周一 到 周五]
//只有不是[法定节假日]的情况下,才会前进一位
if (!SPECIAL_REST_DAYS.contains(DateUtil.formatDate2Str(calendarDate.getTime(), "yyyy-MM-dd"))) {
i++;
}
}
calendarDate.add(Calendar.DATE, 1);
}
//最终返回的日期若是非工作日,还得继续转换为最近的工作日;
return getWorkDate(calendarDate.getTime());
}
/**
* 业务方法方便使用
*
* @param currentDate
* @param delayDays
* @return
*/
public static String getWorkDate2Str(Date currentDate, String delayDays) {
Date workDate = getWorkDate(currentDate, Integer.valueOf(delayDays));
return DateUtil.formatDate2Str(workDate, DateUtil.FORMAT_YYYY_MM_DD);
}
private static void initSpecialDays() {
try {
Properties properties = PropertiesLoaderUtils.loadAllProperties("workDayCalculate.properties");
Set<Object> keySet = properties.keySet();
keySet.forEach(keyObj -> {
//查看特定条件的key
String key = keyObj.toString();
String value = properties.getProperty(key);
if (StringUtils.isNotBlank(value)) {
String[] values = value.split(",");
Collections.addAll(SPECIAL_REST_DAYS, values);
//解析节假日
if (key.contains("restday")) {
Collections.addAll(SPECIAL_REST_DAYS, values);
//解析节假日调休
} else if (key.contains("workday")) {
Collections.addAll(SPECIAL_WORK_DAYS, values);
}
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws ParseException {
Date date = getWorkDate(DateUtils.parseDate("2020-01-24", "yyyy-MM-dd"), 1);
System.out.println(DateFormatUtils.format(date, "yyyy-MM-dd"));
}
}
配置文件:
//法定节假日
restday.yuanDan=2020-01-01
restday.chunJie=2020-01-24,2020-01-25,2020-01-26,2020-01-27,2020-01-28,2020-01-29,2020-01-30
restday.qingMing=2020-04-04,2020-04-05,2020-04-06
restday.laoDong=2020-05-01,2020-05-02,2020-05-03,2020-05-04,2020-05-05
restday.duanWu=2020-06-25,2020-06-26,2020-06-27
restday.guoQing=2020-10-01,2020-10-02,2020-10-03,2020-10-04,2020-10-05,2020-10-06,2020-10-07,2020-10-08
//法定节假日的调休时间
workday.chunJie=2020-01-19,2020-02-01
workday.laoDong=2020-04-26,2020-05-09
workday.duanWu=2020-06-28
workday.guoQing=2020-09-27,2020-10-10