计算两段时间之间的工作日的天/小时/分钟/秒数
我们能经常遇到一些需求,让获取工作日的时间(当然,调休也上班也是工作日哈!),之前自己给这搞得头大,所以整理总结,把该工具类记录下来,希望大家能用到的时候可以直接使用!
在该工具类中主要用到了 java.util 包下的Calendar 这个日期类
大致实现原理:
- 首先我们需要使用两个List将一年的工作日和非工作日的日期存起来,以便后面的判断。
- (SPECIAL_WORK_DAYS)特别的工作日(周六,周日上班的日期集合)
- (SPECIAL_OFF_DAYS)特别的休息日(周一到周五休息的日期集合)
- 其次,定义一个变量,代表工作日的天数(若日期是yy-MM-dd格式的,Integer类型就可以,如果是yy-MM-dd HH:mm:ss格式的,想更精确的,则定义成Double类型即可)。
- workDaysNum (工作日天或小时)
- 其次,获取两个时间段,进行循环比较,判断距现在较远的日期是否是非工作日。(开始时间–startTime,结束时间–endTime)
动手搞起来
新建CalcWorkDaysUtil.java类
方案一:
计算 时间格式为(yy-MM-dd) 的两时间段工作日天数的 完整代码:
/**
* @Title: 计算时间周期之间工作日的时间
* @Author: Sophia
* #Description: CalcWorkDaysUtil
* #Date: 2022/6/2 14:42
*/
public class CalcWorkDaysUtil {
// 定义2个List用于存储特殊的工作日和非工作日
// 特别的工作日(周六,周日上班的日期集合)
private static List<String> SPECIAL_WORK_DAYS = new ArrayList<>();
// 特别的休息日(周一到周五休息的日期集合)
private static List<String> SPECIAL_OFF_DAYS = new ArrayList<>();
// 在这里我就使用静态将2022年所有的节假日存到相应的list中
// 大多是都是从数据库中获取的(这里就这样演示,大家后期可以从数据库获取)
static {
// 特殊的工作日(周六,周日上班的日期集合)
SPECIAL_WORK_DAYS.add("2022-01-29");
SPECIAL_WORK_DAYS.add("2022-01-30");
SPECIAL_WORK_DAYS.add("2022-04-02");
SPECIAL_WORK_DAYS.add("2022-04-24");
SPECIAL_WORK_DAYS.add("2022-05-07");
SPECIAL_WORK_DAYS.add("2022-10-08");
SPECIAL_WORK_DAYS.add("2022-10-09");
// 特殊的休息日(周一到周五休息的日期集合)
SPECIAL_OFF_DAYS.add("2022-01-01");
SPECIAL_OFF_DAYS.add("2022-01-02");
SPECIAL_OFF_DAYS.add("2022-01-03");
SPECIAL_OFF_DAYS.add("2022-01-31");
SPECIAL_OFF_DAYS.add("2022-02-01");
SPECIAL_OFF_DAYS.add("2022-02-02");
SPECIAL_OFF_DAYS.add("2022-02-03");
SPECIAL_OFF_DAYS.add("2022-02-04");
SPECIAL_OFF_DAYS.add("2022-02-05");
SPECIAL_OFF_DAYS.add("2022-02-06");
SPECIAL_OFF_DAYS.add("2022-04-03");
SPECIAL_OFF_DAYS.add("2022-04-04");
SPECIAL_OFF_DAYS.add("2022-04-05");
SPECIAL_OFF_DAYS.add("2022-04-30");
SPECIAL_OFF_DAYS.add("2022-05-01");
SPECIAL_OFF_DAYS.add("2022-05-02");
SPECIAL_OFF_DAYS.add("2022-05-03");
SPECIAL_OFF_DAYS.add("2022-05-04");
SPECIAL_OFF_DAYS.add("2022-06-03");
SPECIAL_OFF_DAYS.add("2022-06-04");
SPECIAL_OFF_DAYS.add("2022-06-05");
SPECIAL_OFF_DAYS.add("2022-09-10");
SPECIAL_OFF_DAYS.add("2022-09-11");
SPECIAL_OFF_DAYS.add("2022-09-12");
SPECIAL_OFF_DAYS.add("2022-10-01");
SPECIAL_OFF_DAYS.add("2022-10-01");
SPECIAL_OFF_DAYS.add("2022-10-02");
SPECIAL_OFF_DAYS.add("2022-10-03");
SPECIAL_OFF_DAYS.add("2022-10-04");
SPECIAL_OFF_DAYS.add("2022-10-05");
SPECIAL_OFF_DAYS.add("2022-10-06");
SPECIAL_OFF_DAYS.add("2022-10-07");
}
// 日期格式化方法(非线程安全的,可根据情况在方法内使用,或使用DateTimeFormatter)
private static SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd");
/**
* 计算两时间段的工作日的天数
* @param startTimeStr 开始时间
* @param endTimeStr 结束时间
* @return
*/
private static Integer getWorkDaysNum(String startTimeStr, String endTimeStr) {
// 准备工作
// 定义Integer类型的工作日天数标识 (注:如果精确小时、分、秒,可用double)
Integer workDaysNum = 0; // 默认0天
// 获取两个日期对象
Calendar cale1 = Calendar.getInstance();
Calendar cale2 = Calendar.getInstance();
// 将开始时间和结束时间转换成Date类型并设置到日历中
try {
// 格式转换
Date startDate = SDF.parse(startTimeStr);
Date endDate = SDF.parse(endTimeStr);
// 设置到日历
cale1.setTime(startDate);
cale2.setTime(endDate);
} catch (ParseException e) {
System.out.println("日期转换异常,请检查日期格式!");
e.printStackTrace();
}
// 准备工作已准备完毕
// 当开始时间小于等于结束时间时,进行循环判断
while (cale1.compareTo(cale2) <= 0) {
// 如果cale1不是周六日, workDaysNum则加一天
// 周日 --> 1,周一 --> 2,周二 --> 3,周三 --> 4,周四 --> 5,周五 --> 6,周六 --> 7
if (cale1.get(Calendar.DAY_OF_WEEK) != 7 && cale1.get(Calendar.DAY_OF_WEEK) != 1) {
// 不是周六日,workDaysNum + 1
workDaysNum++;
// 判断是不是特殊的休息日(周一到周五休息的日期)
//如果不是周六日,判断该日是否属于国家法定节假日或特殊放假日,是 workDaysNum - 1
if (SPECIAL_OFF_DAYS.contains(SDF.format(cale1.getTime()))) {
// 是,则 - 1
workDaysNum--;
}
}
// 如果改日是周六日,则判断改日是否是特别的工作日(周六,周日上班的日期)
if (SPECIAL_WORK_DAYS.contains(SDF.format(cale1.getTime()))) {
// 如果是改日是特殊的工作日,则 + 1
workDaysNum++;
}
// 将时间向后设置一天继续判断
cale1.add(Calendar.DAY_OF_MONTH, 1);
}
// 循环结束,将这两时间段的工作日返回出去
return workDaysNum;
}
public static void main(String[] args) {
String startTimeStr = "2022-05-20";
String endTimeStr = "2022-06-09";
Integer workDaysNum = getWorkDaysNum(startTimeStr, endTimeStr);
System.out.println(startTimeStr + " 到 " + endTimeStr + " 之间的工作日为:" + workDaysNum + "天");
}
}
方案二:
计算 时间格式为(yy-MM-dd HH:mm:ss) 的两时间段工作日的天/小时/分钟/秒数的 完整代码:
package fit.clover.blog.util;
import org.apache.commons.lang3.StringUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
/**
* @Title: 计算时间周期之间工作日的时间
* @Author: Sophia
* #Description: CalcWorkDaysUtil
* #Date: 2022/6/2 14:42
*/
public class CalcWorkDaysUtil {
// 定义2个List用于存储特殊的工作日和非工作日
// 特别的工作日(周六,周日上班的日期集合)
private static List<String> SPECIAL_WORK_DAYS = new ArrayList<>();
// 特别的休息日(周一到周五休息的日期集合)
private static List<String> SPECIAL_OFF_DAYS = new ArrayList<>();
// 在这里我就使用静态将2022年所有的节假日存到相应的list中
// 大多是都是从数据库中获取的(这里就这样演示,大家后期可以从数据库获取)
static {
// 特殊的工作日(周六,周日上班的日期集合)
SPECIAL_WORK_DAYS.add("2022-01-29");
SPECIAL_WORK_DAYS.add("2022-01-30");
SPECIAL_WORK_DAYS.add("2022-04-02");
SPECIAL_WORK_DAYS.add("2022-04-24");
SPECIAL_WORK_DAYS.add("2022-05-07");
SPECIAL_WORK_DAYS.add("2022-10-08");
SPECIAL_WORK_DAYS.add("2022-10-09");
// 特殊的休息日(周一到周五休息的日期集合)
SPECIAL_OFF_DAYS.add("2022-01-01");
SPECIAL_OFF_DAYS.add("2022-01-02");
SPECIAL_OFF_DAYS.add("2022-01-03");
SPECIAL_OFF_DAYS.add("2022-01-31");
SPECIAL_OFF_DAYS.add("2022-02-01");
SPECIAL_OFF_DAYS.add("2022-02-02");
SPECIAL_OFF_DAYS.add("2022-02-03");
SPECIAL_OFF_DAYS.add("2022-02-04");
SPECIAL_OFF_DAYS.add("2022-02-05");
SPECIAL_OFF_DAYS.add("2022-02-06");
SPECIAL_OFF_DAYS.add("2022-04-03");
SPECIAL_OFF_DAYS.add("2022-04-04");
SPECIAL_OFF_DAYS.add("2022-04-05");
SPECIAL_OFF_DAYS.add("2022-04-30");
SPECIAL_OFF_DAYS.add("2022-05-01");
SPECIAL_OFF_DAYS.add("2022-05-02");
SPECIAL_OFF_DAYS.add("2022-05-03");
SPECIAL_OFF_DAYS.add("2022-05-04");
SPECIAL_OFF_DAYS.add("2022-06-03");
SPECIAL_OFF_DAYS.add("2022-06-04");
SPECIAL_OFF_DAYS.add("2022-06-05");
SPECIAL_OFF_DAYS.add("2022-09-10");
SPECIAL_OFF_DAYS.add("2022-09-11");
SPECIAL_OFF_DAYS.add("2022-09-12");
SPECIAL_OFF_DAYS.add("2022-10-01");
SPECIAL_OFF_DAYS.add("2022-10-01");
SPECIAL_OFF_DAYS.add("2022-10-02");
SPECIAL_OFF_DAYS.add("2022-10-03");
SPECIAL_OFF_DAYS.add("2022-10-04");
SPECIAL_OFF_DAYS.add("2022-10-05");
SPECIAL_OFF_DAYS.add("2022-10-06");
SPECIAL_OFF_DAYS.add("2022-10-07");
}
// 日期格式化方法(非线程安全的,可根据情况在方法内使用,或使用DateTimeFormatter)
private static SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd");
private static SimpleDateFormat SDFS = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 计算两时间段的工作日的天/小时/分钟/秒数
* @param startTimeStr 开始时间
* @param endTimeStr 结束时间
* @param flag d为天,h为小时,m为分钟,s为秒
* @return
*/
private static Double getWorkDaysNum(String startTimeStr, String endTimeStr, String flag) {
// 准备工作
// 定义Long类型的工作日天数标识 (注:如果精确小时、分、秒,可用double)
Long workDaysNum = 0l; // 默认0秒
// 定义Double类型的工作日秒数标识
Double workDaysSecond = 0d; // 默认0秒
// 获取两个日期对象
Calendar cale1 = Calendar.getInstance();
Calendar cale2 = Calendar.getInstance();
// 将开始时间和结束时间转换成Date类型并设置到日历中
try {
// 设置到日历
cale1.setTime(SDF.parse(startTimeStr));
cale2.setTime(SDF.parse(endTimeStr));
// 格式转换
Date startDate = SDFS.parse(startTimeStr);
Date endDate = SDFS.parse(endTimeStr);
// 设置时间开始区间的0时,如startDateStr 为2022-05-10 10:00:00, 则距2022-05-10 0:00:00点为10个小时,也是600分钟,也是36000秒
startTimeStr = StringUtils.substringBeforeLast(startTimeStr, " ") + " 00:00:00";
// 设置时间结束区间的24时,如endTimeStr 为2022-06-06 12:00:00 则距2022-06-06 23:59:59点为12个小时,也是720分钟,也是43200秒
endTimeStr = StringUtils.substringBeforeLast(endTimeStr, " ") + " 23:59:59";
Date startDateZero = SDFS.parse(startTimeStr);
Date endDateZero = SDFS.parse(endTimeStr);
// 计算开始区间时间距0时的毫秒值
long startLong = (startDate.getTime() - startDateZero.getTime());
// 计算结束区间时间距24时的毫秒值
long endLong = (endDateZero.getTime() - endDate.getTime());
// 准备工作已准备完毕
// 当开始时间小于等于结束时间时,进行循环判断
while (cale1.compareTo(cale2) <= 0) {
// 如果cale1不是周六日, workDaysNum则加一天
// 周日 --> 1,周一 --> 2,周二 --> 3,周三 --> 4,周四 --> 5,周五 --> 6,周六 --> 7
if (cale1.get(Calendar.DAY_OF_WEEK) != 7 && cale1.get(Calendar.DAY_OF_WEEK) != 1) {
// 不是周六日,workDaysNum + 1
workDaysNum++;
// 判断是不是特殊的休息日(周一到周五休息的日期)
//如果不是周六日,判断该日是否属于国家法定节假日或特殊放假日,是 workDaysNum - 1
if (SPECIAL_OFF_DAYS.contains(SDF.format(cale1.getTime()))) {
// 是,则 - 1
workDaysNum--;
}
}
// 如果改日是周六日,则判断改日是否是特别的工作日(周六,周日上班的日期)
if (SPECIAL_WORK_DAYS.contains(SDF.format(cale1.getTime()))) {
// 如果是改日是特殊的工作日,则 + 1
workDaysNum++;
}
// 将时间向后设置一天继续判断
cale1.add(Calendar.DAY_OF_MONTH, 1);
}
// 将天转成毫秒值
workDaysNum = workDaysNum * 86400000; // 一天等于86400000毫秒
// 天数计算完毕,使用BigDecimal将天数换成秒
BigDecimal daysNumDec = new BigDecimal(workDaysNum);
// 将天转换成秒
BigDecimal tempNum = daysNumDec;// .multiply(secondDec);
tempNum = tempNum.subtract(new BigDecimal(startLong));
tempNum = tempNum.subtract(new BigDecimal(endLong));
if ("d".equals(flag)) {
// 天
workDaysSecond = tempNum.divide(new BigDecimal("86400000"), 2, RoundingMode.HALF_UP).doubleValue(); // 24*60*60秒 = 1天
} else if ("h".equals(flag)) {
// 小时
workDaysSecond = tempNum.divide(new BigDecimal("3600000"), 2, RoundingMode.HALF_UP).doubleValue(); // 60*60秒 = 1小时
} else if ("m".equals(flag)) {
// 分钟
workDaysSecond = tempNum.divide(new BigDecimal("60000"), 2, RoundingMode.HALF_UP).doubleValue(); // 60秒 = 1分钟
} else if ("s".equals(flag)) {
// 秒
workDaysSecond = tempNum.divide(new BigDecimal("1000"), 2, RoundingMode.HALF_UP).doubleValue();
}
} catch (ParseException e) {
System.out.println("日期转换异常,请检查日期格式!");
e.printStackTrace();
}
// 循环结束,将这两时间段的工作日返回出去
return workDaysSecond;
}
public static void main(String[] args) {
String startTimeStr = "2022-06-01 03:00:00";
String endTimeStr = "2022-06-06 12:00:00";
Double days = getWorkDaysNum(startTimeStr, endTimeStr, "d");
Double hour = getWorkDaysNum(startTimeStr, endTimeStr, "h");
Double min = getWorkDaysNum(startTimeStr, endTimeStr, "m");
Double second = getWorkDaysNum(startTimeStr, endTimeStr, "s");
System.out.println(startTimeStr + " 到 " + endTimeStr + " 之间的工作日为:" + days + "天");
System.out.println(startTimeStr + " 到 " + endTimeStr + " 之间的工作日为:" + hour + "小时");
System.out.println(startTimeStr + " 到 " + endTimeStr + " 之间的工作日为:" + min + "分钟");
System.out.println(startTimeStr + " 到 " + endTimeStr + " 之间的工作日为:" + second + "秒");
}
}
运行结果: