package com.boco.nbd.wios.flow.util; import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.Week; import lombok.extern.slf4j.Slf4j; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.LinkedList; import java.util.List; /** * @Description: TODO * @Author: ZQY * @Date: 2022/8/23 **/ @Slf4j public class CalculateHours { SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); String beginWork = "08:30:00"; String endWork = "18:00:00"; //设置上班时间:该处时间可以根据实际情况进行调整 /** * 星期六 */ int saturday=6; /** * 星期天 */ int sunday=0; /** * 上午上班时间,小时 */ int abh = 8; /** * 上午上班时间,分钟 */ int abm = 30; /** * 上午下班时间,小时 */ int aeh = 12; /** * 上午下班时间,分钟 */ int aem = 0; /** * 下午上班时间,小时 */ int pbh = 12; /** * 下午上班时间,小时 */ int pbm = 0; /** * 下午下班时间,小时 */ int peh = 18; /** * 下午下班时间,分钟 */ int pem = 0; float h1 = abh+(float)abm/60; float h2 = aeh+(float)aem/60; float h3 = pbh+(float)pbm/60; float h4 = peh+(float)pem/60; /** * 每天上班小时数 */ float hoursPerDay = h2-h1+(h4-h3); /** * 每周工作天数 */ int daysPerWeek = 5; /** * 每天的毫秒数 */ long milsecPerDay = 1000*60*60*24; /** * 每星期小时数 */ float hoursPerWeek = hoursPerDay*daysPerWeek; public float calculateHours(String beginTime, String endTime){ //对输入的字符串形式的时间进行转换 //真实开始时间 Date t1 = stringToDate(beginTime); //真实结束时间 Date t2 = stringToDate(endTime); //对时间进行预处理 t1 = processBeginTime(t1,0); t2 = processEndTime(t2,0); //若开始时间晚于结束时间,返回0 if(t1.getTime()>t2.getTime()){ return 0; } //开始时间到结束时间的完整星期数 int weekCount = (int) ((t2.getTime()-t1.getTime())/(milsecPerDay*7)); float totalHours = 0; totalHours += weekCount * hoursPerWeek; //调整结束时间,使开始时间和结束时间在一个星期的周期之内 t2.setTime(t2.getTime()-weekCount*7*milsecPerDay); //记录开始时间和结束时间之间工作日天数 int dayCounts = 0; //调整开始时间,使得开始时间和结束时间在同一天,或者相邻的工作日内。 while(t1.getTime()<=t2.getTime()){ Date temp = new Date(t1.getTime()+milsecPerDay); temp = processBeginTime(temp,0); temp.setHours(t1.getHours()); temp.setMinutes(t1.getMinutes()); if(temp.getTime()>t2.getTime()){ break; }else{ t1 = temp; dayCounts++; } } totalHours += dayCounts * hoursPerDay; float hh1 = t1.getHours() + (float)t1.getMinutes()/60 + (float)t1.getSeconds()/3600; float hh2 = t2.getHours() + (float)t2.getMinutes()/60 + (float)t2.getSeconds()/3600; //处理开始结束是同一天的情况 if(t1.getDay()==t2.getDay()){ float tt = 0; tt = hh2 - hh1; if(hh1>=h1&&hh1<=h2&&hh2>=h3){ tt = tt - (h3-h2); } totalHours += tt; }else{ //处理开始结束不是同一天的情况 float tt1 = h4 - hh1; float tt2 = hh2 - h1; if(hh1<=h2){ tt1 = tt1 - (h3-h2); } if(hh2>=h3){ tt2 = tt2 - (h3-h2); } totalHours += (tt1 + tt2); } return totalHours; } /** * 格式化输出时间: yyyy-mm-dd hh:mm:ss 星期x * @param t * @return */ private static String printDate(Date t) { String str; String xingqi = null; switch (t.getDay()) { case 0: xingqi = "星期天"; break; case 1: xingqi = "星期一"; break; case 2: xingqi = "星期二"; break; case 3: xingqi = "星期三"; break; case 4: xingqi = "星期四"; break; case 5: xingqi = "星期五"; break; case 6: xingqi = "星期六"; break; default: break; } str = xingqi; return str; } /** * 对结束时间进行预处理,使其处于工作日内的工作时间段内 * @param t * @return */ private Date processEndTime(Date t,int type) { float h = t.getHours() + (float)t.getMinutes()/60; //若结束时间晚于下午下班时间,将其设置为下午下班时间 if(h>=h4){ t.setHours(peh); t.setMinutes(pem); }else { //若结束时间介于中午休息时间,那么设置为上午下班时间 if(h>=h2&&h<=h3){ t.setHours(aeh); t.setMinutes(aem); }else{ //若结束时间早于上午上班时间,日期向前推一天,并将时间设置为下午下班时间 if(h<=h1){ t.setTime(t.getTime()-milsecPerDay); t.setHours(peh); t.setMinutes(pem); } } } //若结束时间是周末,那么将结束时间向前推移到最近的工作日的下午下班时间 if(type==0){ if(t.getDay()==sunday){ t.setTime(t.getTime()-milsecPerDay*(t.getDay()==saturday?1:2)); t.setHours(peh); t.setMinutes(pem); } if(t.getDay()==saturday){ t.setTime(t.getTime()-milsecPerDay*(t.getDay()==saturday?1:2)); t.setHours(peh); t.setMinutes(pem); } } return t; } /** * 对开始时间进行预处理 * @param t * @return */ private Date processBeginTime(Date t,int type) { float h = t.getHours() + (float)t.getMinutes()/60; //若开始时间晚于下午下班时间,将开始时间向后推一天 if(h>=h4){ t.setTime(t.getTime()+milsecPerDay); t.setHours(abh); t.setMinutes(abm); }else { //若开始时间介于中午休息时间,那么设置为下午上班时间 if(h>=h2&&h<=h3){ t.setHours(pbh); t.setMinutes(pbm); }else{ //若开始时间早于上午上班时间,将hour设置为上午上班时间 if(t.getHours()<=h1){ t.setHours(abh); t.setMinutes(abm); } } } //若开始时间是周末,那么将开始时间向后推移到最近的工作日的上午上班时间 if(type==0){ if(t.getDay()==sunday){ t.setTime(t.getTime()+milsecPerDay*(t.getDay()==saturday?2:1)); t.setHours(abh); t.setMinutes(abm); }if(t.getDay()==saturday){ t.setTime(t.getTime()+milsecPerDay*(t.getDay()==saturday?2:1)); t.setHours(abh); t.setMinutes(abm); } } return t; } /** * 将字符串形式的时间转换成Date形式的时间 * @param time * @return */ private Date stringToDate(String time){ try { return format.parse(time); } catch (ParseException e) { e.printStackTrace(); return null; } } /** * 去除周末节假日工作小时 * @param beginTime * @param endTime * @return * @throws ParseException */ public static float calculateHour(String beginTime,String endTime,String[] workdayArr,String[] naturalArr) { try { CalculateHours ch = new CalculateHours(); float a = ch.calculateHours(beginTime, endTime); Calendar startDay = Calendar.getInstance(); Calendar endDay = Calendar.getInstance(); startDay.setTime(FORMATTER.parse(beginTime)); endDay.setTime(FORMATTER.parse(endTime)); Calendar naturalStartDay = Calendar.getInstance(); Calendar naturalEndDay = Calendar.getInstance(); naturalStartDay.setTime(FORMATTER.parse(beginTime)); naturalEndDay.setTime(FORMATTER.parse(endTime)); String[] workday = printDay(startDay, endDay); String[] naturalDay = printNaturalDay(naturalStartDay, naturalEndDay); // String[] holiday = new String[]{"01-01","01-02","01-03","05-01","05-02","05-03","10-01","10-02", // "10-03","10-04","10-05","10-06","02-08","02-09","02-10","09-12","04-04","04-05","04-06"}; // String[] restDay = new String[]{"04-02","04-03"}; // Calendar now = Calendar.getInstance(); // int year=now.get(Calendar.YEAR); //获取当前年份 // // //工作日集合 // List<String> workdayList = new ArrayList<String>(); // for (String string : holiday) { // string=year+"-"+string; // workdayList.add(string); // } // String[] workdayArr = workdayList.toArray(new String[0]); // // //自然日集合 // List<String> naturalList = new ArrayList<String>(); // for (String string : restDay) { // string=year+"-"+string; // naturalList.add(string); // } // String[] naturalArr = naturalList.toArray(new String[0]); List<String> holidayDayList = arrContrastRest(workday, workdayArr); List<String> restDayList = arrContrastRest(naturalDay, naturalArr); Double holidayHous = getRestDayHous(holidayDayList, beginTime, endTime); Double workDayHous = getRestDayHous(restDayList, beginTime, endTime); float b = (float) (Math.round(a * 100)) / 100; float workHours = (float) (b - holidayHous + workDayHous); return workHours; }catch (ParseException parseException){ parseException.printStackTrace(); log.error("工作日计算报错信息",parseException.getMessage()); } return 0; } /** * 计算开始时间和结束时间在调休或者节假日当天精确计算工作时长 * @param dayList * @param beginTime * @param endTime * @return */ private static double getRestDayHous(List<String> dayList,String beginTime,String endTime){ String beginTimeDay=beginTime.substring(0,10); String endTimeDay=endTime.substring(0,10); double time=0; try{ if(dayList.contains(beginTimeDay)||dayList.contains(endTimeDay)){ if(beginTimeDay.equals(endTimeDay)){ time=getRestTime(beginTime,endTime); return time; } double sTime=0; double eTime=0; CalculateHours ch = new CalculateHours(); SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); //开始时间跟上班时间比 if(dayList.contains(beginTimeDay)){ Date t1 = ch.processBeginTime(sdf.parse(beginTime),1); Date t2 = sdf.parse(beginTimeDay+" "+ch.beginWork); Date t3 = sdf.parse(beginTimeDay+" "+ch.endWork); if(t1.getTime()<=t2.getTime()){ sTime=9.5; }else if(t1.getTime()>t2.getTime()){ float hh1 = t1.getHours() + (float)t1.getMinutes()/60; float hh2 = t3.getHours() + (float)t3.getMinutes()/60; float tt = 0; tt = hh2 - hh1; sTime=tt; } } //结束时间跟下班时间比 if(dayList.contains(endTimeDay)){ Date t1 = ch.processEndTime(sdf.parse(endTime),1); Date t2 = sdf.parse(endTimeDay+" "+ch.beginWork); Date t3 = sdf.parse(endTimeDay+" "+ch.endWork); if(t1.getTime()>=t3.getTime()){ eTime=9.5; }else if(t1.getTime()<t3.getTime()){ float hh1 = t1.getHours() + (float)t1.getMinutes()/60; float hh2 = t2.getHours() + (float)t2.getMinutes()/60; float tt = 0; tt = hh1 - hh2; eTime=tt; } } if(sTime>0&&eTime>0){ time=(dayList.size()-2)*9.5+sTime+eTime; }else if(sTime>0&&eTime==0){ time=(dayList.size()-1)*9.5+sTime; }else if(sTime==0&&eTime>0){ time=(dayList.size()-1)*9.5+eTime; } }else{ time=dayList.size()*9.5; } }catch (Exception e){ e.printStackTrace(); } return time; } /** * 获取调休日期工作时间 * @param beginTime * @param endTime * @return */ private static float getRestTime(String beginTime,String endTime){ CalculateHours ch = new CalculateHours(); //对输入的字符串形式的时间进行转换 //真实开始时间 Date t1 = ch.stringToDate(beginTime); //真实结束时间 Date t2 = ch.stringToDate(endTime); //对时间进行预处理 t1 = ch.processBeginTime(t1,1); t2 = ch.processEndTime(t2,1); float hh1 = t1.getHours() + (float)t1.getMinutes()/60 + (float)t1.getSeconds()/3600; float hh2 = t2.getHours() + (float)t2.getMinutes()/60 + (float)t1.getSeconds()/3600; float tt = 0; tt = hh2 - hh1; if(hh1>=ch.h1&&hh1<=ch.h2&&hh2>=ch.h3){ tt = tt - (ch.h3-ch.h2); } return tt; } /** * 去除数组中相同日期 * @param arr1 * @param arr2 * @return */ private static int arrContrast(String[] arr1, String[] arr2){ int count=0; List<String> list = new LinkedList<String>(); //处理第一个数组,list里面的值为1,2,3,4 for (String str : arr1) { if (!list.contains(str)) { list.add(str); } } //如果第二个数组存在和第一个数组相同的值,就删除 for (String str : arr2) { if(list.contains(str)){ list.remove(str); ++count; } } return count; } private static List<String> arrContrastRest(String[] arr1, String[] arr2){ List<String> list = new LinkedList<String>(); List<String> restDayList = new LinkedList<String>(); for (String str : arr1) { if (!list.contains(str)) { list.add(str); } } for (String str : arr2) { if(list.contains(str)){ restDayList.add(str); list.remove(str); } } return restDayList; } private static final DateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd"); /** * 获取工作日 * @param startDay * @param endDay * @return */ private static String[] printDay(Calendar startDay, Calendar endDay) { List<String> list = new ArrayList<String>(); // 给出的日期开始日比终了日大则不执行打印 if (startDay.compareTo(endDay) > 0) { return new String[]{}; }else if(startDay.compareTo(endDay) == 0){ list.add(FORMATTER.format(startDay.getTime())); return list.toArray(new String[0]); } // 现在打印中的日期 Calendar currentPrintDay = startDay; if(!getIsWeekend(currentPrintDay.getTime())){ list.add(FORMATTER.format(currentPrintDay.getTime())); } while (true) { // 日期加一 currentPrintDay.add(Calendar.DATE, 1); if(!getIsWeekend(currentPrintDay.getTime())) { list.add(FORMATTER.format(currentPrintDay.getTime())); } // 日期加一后判断是否达到终了日,达到则终止打印 if (currentPrintDay.compareTo(endDay) == 0) { break; } } String[] arr = list.toArray(new String[0]); return arr; } /** * 获取自然日 * @param startDay * @param endDay * @return */ private static String[] printNaturalDay(Calendar startDay, Calendar endDay) { List<String> list = new ArrayList<String>(); // 给出的日期开始日比终了日大则不执行打印 if (startDay.compareTo(endDay) > 0) { return new String[]{}; }else if(startDay.compareTo(endDay) == 0){ list.add(FORMATTER.format(startDay.getTime())); return list.toArray(new String[0]); } // 现在打印中的日期 Calendar currentPrintDay = startDay; list.add(FORMATTER.format(currentPrintDay.getTime())); while (true) { // 日期加一 currentPrintDay.add(Calendar.DATE, 1); list.add(FORMATTER.format(currentPrintDay.getTime())); // 日期加一后判断是否达到终了日,达到则终止打印 if (currentPrintDay.compareTo(endDay) == 0) { break; } } String[] arr = list.toArray(new String[0]); return arr; } /** * 判断是否周末 * @param date * @return */ private static boolean getIsWeekend(Date date){ Week week= DateUtil.dayOfWeekEnum(date); return (Week.SUNDAY.equals(week)||Week.SATURDAY.equals(week)); } }