1、前言
计算请假时间还是蛮考虑逻辑的,可以借鉴借鉴我的,有啥需要改进的记得留言。
2、代码
2.1 html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<title>计算请假时间</title>
<script src="js/jquery-3.6.1.min.js"></script>
<script src="js/qingjia/xxyLeaveTimeCount.js"></script>
</head>
<style>
.mt-10{
margin-top: 10px;
}
</style>
<body>
<div>
<h2>请假时间</h2>
<div>
<div>开始时间:</div>
<div><input id="startTime" name="startTime" value="2023-09-04 08:30:00"/></div>
</div>
<div class="mt-10">
<div>结束时间:</div>
<div><input id="endTime" name="endTime" value="2023-09-04 18:00:00"/></div>
</div>
<div class="mt-10">
<div>请假类型:</div>
<div>
<select id="vacateType" name="vacateType">
<option value="">请选择</option>
<option value="1">事假</option>
<option value="2">病假</option>
<option value="3">补休</option>
<option value="4">丧假</option>
<option value="5">产假</option>
<option value="6">年休假</option>
<option value="8">婚假</option>
<option value="9">生日假</option>
</select>
</div>
</div>
<div class="mt-10">
<div>休息日请假(勾选后请假时长会包括休息日,请假的时间也不限制):</div>
<div>
<input type="radio" title="是" value="true" name="isMakeUpForWork"/> 是
<input type="radio" title="否" value="false" name="isMakeUpForWork" checked/> 否
</div>
</div>
<div class="mt-10">
<button id="submit">计算</button>
</div>
<div class="mt-10">
<input id="totalLeaveHours" placeholder="请假总时长"/>
</div>
</div>
</body>
<script>
$(function(){
$("#submit").click(function(){
var startTime=$("#startTime").val();
var endTime=$("#endTime").val();
var vacateType=$("#vacateType").val();
if(!vacateType){
alert("请选择请假类型")
return;
}
var isMakeUpForWork=$("input[name='isMakeUpForWork']:checked").val();
//周末也计算到内
var totalLeaveHours=getLeaveHours(startTime,endTime,vacateType,isMakeUpForWork);
$("#totalLeaveHours").val(totalLeaveHours);
})
})
</script>
</html>
2.2 js
/**
* 计算请假时长工具类
*/
/**
* 计算所用常量
*/
const countConst={
"workHours":7,//每天的工作时长
"noonStartBreakTime":12,//午休开始时间 12:00
"noonEndBreakTime":14.5,//午休结束时间 14:30
"workStartTime":8.5,//上班时间 8:30
"workEndTime":18,//下班时间 18:00
"morningHours":3.5,//上午上班时长
"afternoonHours":3.5,//下午上班时长
"noonHours":2.5//午休总时长
};
/**
* 计算请假时长
* @param {string} start 请假开始时间 格式 2023-01-01 08:00:00
* @param {string} end 请假结束时间
* @param {string} vacateType 请假类型
* @param {string} isMakeUpForWork 是否补班时段请假,true才能选择休息日请假
*/
function getLeaveHours(start,end,vacateType,isMakeUpForWork){
console.log(start);
console.log(end);
//请假总时长
let totalDurationLeave=0;
//请假开始和结束时间必须在8:30-12:00或14:30-18:00里面
var startTime = new Date(start.replace(/-/g, '/'));
var endTime = new Date(end.replace(/-/g, '/'));
//验证请假时间是否符合
if(!verifyTime(startTime,vacateType,isMakeUpForWork) || !verifyTime(endTime,vacateType,isMakeUpForWork)){
// alert("请假时间必须在8:30-12:00或14:30-18:00");
return 0;
}
if(endTime.getTime()<startTime.getTime()){
alert("开始时间必须小于结束时间!");
return 0;
}
//两时间的毫秒差
let ms = Math.abs(endTime.getTime() - startTime.getTime());
// console.log("毫秒差="+ms);
//相差时长
let subtractionHours=accDiv(ms,1000*60*60);
// console.log("时间差="+subtractionHours);
if(subtractionHours<2){
alert("请假时长至少两小时!");
return 0;
}
//需要减去的午休总时长
let subtractNoonHours=0;
//开始时间的几号
let startDay=startTime.getDate();
// console.log("startDay",startDay);
//结束时间的几号
let endDay=endTime.getDate();
// console.log("endDay",endDay);
//开始时间的小时
let startHours=startTime.getHours();
//结束时间的小时
let endHours=endTime.getHours();
//开始时间的分钟
let startMinutes=startTime.getMinutes();
//结束时间的分钟
let endMinutes=endTime.getMinutes();
//日期差
let subtractDay=endDay-startDay;
//请1天及以内
if(subtractionHours<24 && subtractDay==0){
//这属于跨午休了
if(startHours<=12&&endHours>=14){
subtractNoonHours=countConst.noonHours;
}
totalDurationLeave=accSub(subtractionHours,subtractNoonHours);
return totalDurationLeave;
}else{//请1天以上
//请假天数=日期差+1;
let day=accAdd(subtractDay,1);
// console.log("day===");
// console.log(day);
/**
* 计算第一天请假时长
*/
//分钟转换成小时
var minutesToHours=accDiv(startMinutes,60);
// console.log("转换分钟:"+minutesToHours);
//请假小时数 例如:8:30为8.5
let morningH=accAdd(startHours,minutesToHours);
// console.log("请假小时:"+morningH);
//第一天请假时长
let oneDayLeaveHours=getOneDayLeaveDuration(startHours,morningH);
// console.log("第一天请假时长:"+oneDayLeaveHours);
/**
* 计算最后一天请假时长
*/
//分钟转换成小时
var endMinutesToHours=accDiv(endMinutes,60);
// console.log("转换分钟:"+endMinutesToHours);
//上午请假时长
let endMorningH=accAdd(endHours,endMinutesToHours);
//请假小时数 例如:8:30为8.5
// console.log("请假小时:"+endMorningH);
//最后一天请假时长
let endDayLeaveHours=getEndDayLeaveDuration(endHours,endMorningH);
// console.log("最后一天请假时长:"+endDayLeaveHours);
//中间跨的天数=(总跨天数-前后天数(2天))*每天工作时长
let daySpan=accSub(day,2);
daySpan=accMul(daySpan,countConst.workHours);
//请假总时长=第一天请假时长+(总跨天数-2)*每天工作时长+最后一天请假时长
totalDurationLeave=accAdd(oneDayLeaveHours,endDayLeaveHours);
totalDurationLeave=accAdd(totalDurationLeave,daySpan);
/**
* 只有产假会把星期天和星期六算进去
* 周末排除
*
*/
if(vacateType!='5' && isMakeUpForWork=="false"){
let restDay=weekendBetween(start,end);
// console.log("其中休息日的天数");
// console.log(restDay);
//休息日大于0 不是产假
if(restDay>0){
//实际请假总时长=请假总时长-(休息日*每天工作时长)
let restDayTime=accMul(restDay,countConst.workHours);
totalDurationLeave=accSub(totalDurationLeave,restDayTime);
}
}
return totalDurationLeave;
}
}
/**
* 判断请假时间里面有多少个周末
*/
// console.log("09-04~10-20");
// console.log(weekendBetween("2023-09-01 08:30:00","2023-10-20 18:00:00"));
function weekendBetween(dtStart, dtEnd) {
//休息日
let tRestDay=0;
/**
* 时间处理,把时分秒设为一致的,方便计算
*/
var dataStartRest=dtStart.split(" ");
var restStartDate=dataStartRest[0];
var dataEndRest=dtEnd.split(" ");
var restEndDate=dataEndRest[0];
var restStartDateStr=restStartDate+" 08:00:00";
var restEndDateStr=restEndDate+" 08:00:00";
var startTime = new Date(restStartDateStr.replace(/-/g, '/'));
var endTime = new Date(restEndDateStr.replace(/-/g, '/'));
/**
* 获取时间的各种值
*/
//星期
let dayStart=startTime.getDay();
let dayEnd=endTime.getDay();
//日期
let dateStart=startTime.getDate();
let dateEnd=endTime.getDate();
//一个小时的毫秒数
let hoursMs=1000*60*60;
//一天的毫秒数
let dayMs=hoursMs*24;
//日期转为毫秒
let timeStart=startTime.getTime();
let timeEnd=endTime.getTime();
//两时间的毫秒差
let ms = Math.abs(endTime.getTime() - startTime.getTime());
//间隔小时
let intervalHours = accDiv(ms,hoursMs);
//间隔天数
let intervalDay = accDiv(ms,dayMs);
let dateTimeStr=endTime.getTime();
var dateTime="";
//计算间隔中有多少休息日
for (var i = 0; i < intervalDay; i++) {
//第一次以默认值计算
if(i!=0){
dateTimeStr=accSub(dateTimeStr,dayMs);
}
dateTime=getSpeTime(dateTimeStr);
var dateNowTime = new Date(dateTime.replace(/-/g, '/'));
if(dateNowTime.getDay()==6||dateNowTime.getDay()==0){
// console.log(dateTime);
tRestDay++;
}
}
return tRestDay;
}
function getSpeTime(timeStr) {
var dateSpe = new Date(parseInt(timeStr));
// console.log("dateSpe====")
// console.log(dateSpe)
// timeStr是毫秒值
// 获取年份
var year=dateSpe.getFullYear();
//获取月份,获取的月份比实际小1,所以需要+1
var month=dateSpe.getMonth()+1;
if (month < 10) {
month = '0' + month
}
//获取日
var date=dateSpe.getDate();
if (date < 10) {
date = '0' + date
}
//获取时
var hours=dateSpe.getHours();
if (hours < 10) {
hours = '0' + hours
}
//获取分
var minutes=dateSpe.getMinutes();
if (minutes < 10) {
minutes = '0' + minutes
}
//获取秒
var seconds=dateSpe.getSeconds();
if (seconds < 10) {
seconds = '0' + seconds
}
//组合格式为年-月-日 时:分:秒(2022-6-6 12:12:12)
return year+"-"+month+"-"+date+" "+hours+":"+minutes+":"+seconds;
}
/**
* 毫秒数转换成日期格式
* @param {Object} milliseconds
*/
function convertMillisecondsToDate(milliseconds) {
const date = new Date(milliseconds);
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
const hours = date.getHours();
const minutes = date.getMinutes();
const seconds = date.getSeconds();
let dateString = `${year}-${month < 10 ? '0' + month : month}-${day < 10 ? '0' + day : day} ${hours < 10 ? '0' + hours : hours}:${minutes < 10 ? '0' + minutes : minutes}:${seconds < 10 ? '0' + seconds : seconds}`;
return dateString;
}
/**
* 获取第一天请假时长
* @param {Object} hours 请假的小时 例如:8:30记作8
* @param {Object} realityLeaveHours 实际请假的小时 例如:8:30记作8.5
*/
function getOneDayLeaveDuration(hours,realityLeaveHours){
//上午或下午请假时长
let noeMorningHours=0;
//请假实际时长
let leaveHours=0;
//判断请假时间是上午还是下午
//请假时间<=上午下班时间 是上午请假
if(hours<=countConst.noonStartBreakTime){
//用上午下班时间-实际请假的小时=早上请假时长
noeMorningHours=accSub(countConst.noonStartBreakTime,realityLeaveHours);
//第一天请假时长=下午工作时长+早上请假时长
leaveHours=accAdd(countConst.afternoonHours,noeMorningHours);
}else{//下午请假
//下午请假时长=下午下班时间-实际请假的小时
noeMorningHours=accSub(countConst.workEndTime,realityLeaveHours);
//第一天请假时长=下午请假时长
leaveHours=noeMorningHours;
}
return leaveHours;
}
/**
* 获取最后一天请假时长
* @param {Object} hours 请假的小时 例如:8:30记作8
* @param {Object} realityLeaveHours 实际请假的小时 例如:8:30记作8.5
*/
function getEndDayLeaveDuration(hours,realityLeaveHours){
//上午或下午请假时长
let noeMorningHours=0;
//请假实际时长
let leaveHours=0;
//判断请假时间是上午还是下午
//请假时间<=上午下班时间 是上午请假
if(hours<=countConst.noonStartBreakTime){
//早上请假时长=请假小时-早上上班时间
noeMorningHours=accSub(realityLeaveHours,countConst.workStartTime);
//最后一天请假时长=早上请假时长
leaveHours=noeMorningHours;
}else{//下午请假
//下午请假时长=实际请假的小时-下午上班时间
noeMorningHours=accSub(realityLeaveHours,countConst.noonEndBreakTime);
//最后一天请假时长=下午请假时长+上午工作小时(3.5小时)
leaveHours=accAdd(noeMorningHours,countConst.morningHours);
}
return leaveHours;
}
/**
* 验证请假时间是否在工作时间段
* @param {Object} time 请假时间
*/
function verifyTime(time,vacateType,isMakeUpForWork){
//小时
let tHours=time.getHours();
//分钟
let tMinutes=time.getMinutes();
//分钟转换成小时
let minutesToHours=accDiv(tMinutes,60);
//请假小时数 例如:8:30为8.5
let leaveH=accAdd(tHours,minutesToHours);
//请假时间是否在工作时间
var isWorkTime=true;
//不在工作时间
if(leaveH<countConst.workStartTime||leaveH>countConst.workEndTime){
alert("请假时间必须在8:30-12:00或14:30-18:00!")
isWorkTime=false;
}
if (time.getSeconds()!=0) {
alert("为了更准确的计算请假时长,请假时间的秒数请设置为00!")
isWorkTime=false;
}
//不是产假的要判断请假时间不能选择星期六、星期天
// console.log("vacateType====")
// console.log(vacateType)
// console.log(isMakeUpForWork)
if(vacateType!='5' && isMakeUpForWork=="false"){
if (time.getDay()==6||time.getDay()==0) {
alert("请假时间不能选休息日(产假或勾选休息日请假除外)!")
isWorkTime=false;
}
}
//处于午休时间段
if(leaveH>countConst.noonStartBreakTime && leaveH<countConst.noonEndBreakTime){
// console.log("处于午休时间段");
isWorkTime=false;
}
// console.log("请假时间是否符合:"+isWorkTime);
return isWorkTime;
}
/**
** 加法函数,用来得到精确的加法结果
** 说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
** 调用:accAdd(arg1,arg2)
** 返回值:arg1加上arg2的精确结果
**/
function accAdd(arg1, arg2) {
var r1, r2, m, c;
try {
r1 = arg1.toString().split(".")[1].length;
}
catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split(".")[1].length;
}
catch (e) {
r2 = 0;
}
c = Math.abs(r1 - r2);
m = Math.pow(10, Math.max(r1, r2));
if (c > 0) {
var cm = Math.pow(10, c);
if (r1 > r2) {
arg1 = Number(arg1.toString().replace(".", ""));
arg2 = Number(arg2.toString().replace(".", "")) * cm;
} else {
arg1 = Number(arg1.toString().replace(".", "")) * cm;
arg2 = Number(arg2.toString().replace(".", ""));
}
} else {
arg1 = Number(arg1.toString().replace(".", ""));
arg2 = Number(arg2.toString().replace(".", ""));
}
return (arg1 + arg2) / m;
}
/**
** 减法函数,用来得到精确的减法结果
** 说明:javascript的减法结果会有误差,在两个浮点数相减的时候会比较明显。这个函数返回较为精确的减法结果。
** 调用:accSub(arg1,arg2)
** 返回值:arg1减去arg2的精确结果
**/
function accSub(arg1, arg2) {
var r1, r2, m, n;
try {
r1 = arg1.toString().split(".")[1].length;
}
catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split(".")[1].length;
}
catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2)); //last modify by deeka //动态控制精度长度
n = (r1 >= r2) ? r1 : r2;
return ((arg1 * m - arg2 * m) / m).toFixed(n);
}
/**
** 乘法函数,用来得到精确的乘法结果
** 说明:javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。
** 调用:accMul(arg1,arg2)
** 返回值:arg1乘以 arg2的精确结果
**/
function accMul(arg1, arg2) {
var m = 0, s1 = arg1.toString(), s2 = arg2.toString();
try {
m += s1.split(".")[1].length;
}
catch (e) {
}
try {
m += s2.split(".")[1].length;
}
catch (e) {
}
return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
}
/**
** 除法函数,用来得到精确的除法结果
** 说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果。
** 调用:accDiv(arg1,arg2)
** 返回值:arg1除以arg2的精确结果
**/
function accDiv(arg1, arg2) {
var t1 = 0, t2 = 0, r1, r2;
try {
t1 = arg1.toString().split(".")[1].length;
}
catch (e) {
}
try {
t2 = arg2.toString().split(".")[1].length;
}
catch (e) {
}
with (Math) {
r1 = Number(arg1.toString().replace(".", ""));
r2 = Number(arg2.toString().replace(".", ""));
return (r1 / r2) * pow(10, t2 - t1);
}
}