闲来无事将之前用Oracle写的算法重新用Python编写了一遍,通过使用interval模块进一步简化了流程。
初始化参数
因为夏季作息和冬季作息时间表不一致,需要提前声明:
import datetime
import interval
# 设置夏季作息
# 夏季作息开始日期为 当年的5月1日
summerstartdatestring = '-'.join((datetime.datetime.strftime(datetime.datetime.now(),'%Y'),)+('5','1'))
summerstartdate = datetime.datetime.strptime(summerstartdatestring,'%Y-%m-%d')
# 夏季作息截止日期为 当年的10月1日
summerenddatestring = '-'.join((datetime.datetime.strftime(datetime.datetime.now(),'%Y'),)+('10','1'))
summerenddate = datetime.datetime.strptime(summerenddatestring,'%Y-%m-%d')
# 夏季作息时间区间(闭合区间)
summer = interval.Interval(summerstartdate,summerenddate)
# 设置作息时间
# 夏季作息时间安排
summermorningstart = '08:30:00'
summermorningend = '12:00:00'
summerafternoonstart = '14:30:00'
summerafternoonend = '18:00:00'
# 夏季作息,实际工作秒
summerseconds = int(60 * 60 * 7)
# 冬季作息时间安排
wintermorningstart = '08:30:00'
wintermorningend = '11:30:00'
winterafternoonstart = '14:00:00'
winterafternoonend = '17:30:00'
# 冬季作息,实际工作秒
winterseconds = int(60 * 60 * 6.5)
主函数
通过递归调用计算请假区间的有效时长,返回有效天数和秒数的元祖
def leaveBillTime(_starttime,_endtime):
'''
根据请假时间段计算请假时间
:param _starttime: 起始时间
:param _endtime: 结束时间
:return: 请假时长(天,秒)
'''
# 日期格式转换
leavestarttime = toDateTime(_starttime)
leaveendtime = toDateTime(_endtime)
# 获取实际请假时间点
leavestarttime = effectiveStartTime(leavestarttime)
leaveendtime = effectiveEndTime(leaveendtime)
# 生成请假时间区间
leavebilltime = interval.Interval(leavestarttime,leaveendtime)
if leavebilltime in summer:
# 请假时间区间在夏季作息区间
leavebillday = (leaveendtime - leavestarttime).days
if datetime.datetime.strftime(leavestarttime + datetime.timedelta(leavebillday),'%Y-%m-%d') == datetime.datetime.strftime(leaveendtime,'%Y-%m-%d'):
# 夏季作息,起始时间点小于等于截止时间点
# 返回实际(请假天数,实际请假秒数)
return (leavebillday + round((effectiveStartSeconds(leavestarttime)+effectiveEndSeconds(leaveendtime)) / summerseconds,2) - 1,
summerseconds * (leavebillday - 1) + effectiveStartSeconds(leavestarttime) + effectiveEndSeconds(leaveendtime))
else:
# 夏季作息,起始时间点大于截止时间点
# 返回实际(请假天数,实际请假秒数)
return (leavebillday + round((effectiveStartSeconds(leavestarttime) + effectiveEndSeconds(leaveendtime)) / summerseconds, 2),
summerseconds * leavebillday + effectiveStartSeconds(leavestarttime) + effectiveEndSeconds(leaveendtime))
elif summer in leavebilltime:
# 请假时间区间横跨夏季作息时间
# 使用递归调用将时间划分为夏季作息开始前,夏季作息,夏季作息后
# 夏季作息前
summerbefortime = leaveBillTime(leavestarttime,summerstartdate)
summertime = leaveBillTime(summerstartdate,summerenddate)
summeraftertime = leaveBillTime(summerenddate,leaveendtime)
return (summerbefortime[0] + summertime[0] + summeraftertime[0],
summerbefortime[1] + summertime[1] + summeraftertime[1])
elif not leavebilltime.overlaps(summer):
# 请假时间区间在冬季作息
leavebillday = (leaveendtime - leavestarttime).days
if datetime.datetime.strftime(leavestarttime + datetime.timedelta(leavebillday),'%Y-%m-%d') == datetime.datetime.strftime(leaveendtime,'%Y-%m-%d'):
# 冬季作息,请假起始时间点小于等于截止时间点
# 返回实际(请假天数,实际请假秒数)
return (leavebillday + round((effectiveStartSeconds(leavestarttime)+effectiveEndSeconds(leaveendtime)) / winterseconds,2)-1,
winterseconds * (leavebillday - 1) + effectiveStartSeconds(leavestarttime)+effectiveEndSeconds(leaveendtime))
else:
# 冬季作息,请假起始时间点小于等于截止时间点
# 返回实际(请假天数,实际请假秒数)
return (leavebillday + round(
(effectiveStartSeconds(leavestarttime) + effectiveEndSeconds(leaveendtime)) / winterseconds, 2),
winterseconds * leavebillday + effectiveStartSeconds(leavestarttime) + effectiveEndSeconds(
leaveendtime))
else:
# 请假时间区间横跨冬季夏季作息
if leavestarttime in summer:
# 起始时间在夏季作息
summerbilltime = leaveBillTime(leavestarttime,summerenddate)
winterbilltime = leavebilltime(summerenddate,leaveendtime)
return (summerbilltime[0] + winterbilltime[0],
summerbilltime[1] + winterbilltime[1])
if leaveendtime in summer:
# 截止时间在夏季作息
summerbilltime = leaveBillTime(summerstartdate, leaveendtime)
winterbilltime = leavebilltime(leavestarttime, summerstartdate)
return (summerbilltime[0] + winterbilltime[0],
summerbilltime[1] + winterbilltime[1])
辅助函数
1,请假开始当日的剩余秒数
def effectiveStartSeconds(_datetime):
'''
计算请假起始当天的剩余有效请假时间
:param _datetime: 请假起始当天的时间
:return: 剩余秒
'''
# 判断是否是夏季作息
summerflag = isSummer(_datetime)
if summerflag:
# 夏季作息
if _datetime <= onworkTime(_datetime,summermorningend):
# 夏季作息,上午请假返回上午剩余时间 + 下午班时长
return (onworkTime(_datetime,summermorningend)-_datetime).seconds + (onworkTime(_datetime,summerafternoonend)-onworkTime(_datetime,summerafternoonstart)).seconds
else:
# 夏季作息,下午请假返回下午剩余时长
return (onworkTime(_datetime, summerafternoonend) - _datetime).seconds
else:
# 冬季作息
if _datetime <= onworkTime(_datetime, wintermorningend):
# 冬季作息,上午请假返回上午剩余时间 + 下午班时长
return (onworkTime(_datetime, wintermorningend) - _datetime).seconds + (
onworkTime(_datetime, winterafternoonend) - onworkTime(_datetime, winterafternoonstart)).seconds
else:
# 冬季作息,下午请假返回下午剩余时长
return (onworkTime(_datetime, winterafternoonend) - _datetime).seconds
2,请假截止日期用掉的秒数
def effectiveEndSeconds(_datetime):
'''
计算请假截止当天的剩余有效请假时间
:param _datetime: 请假截止当天的时间
:return: 剩余秒
'''
# 判断是否是夏季作息
summerflag = isSummer(_datetime)
if summerflag:
# 夏季作息
if _datetime <= onworkTime(_datetime,summermorningend):
# 夏季作息,上午请假返回上午请假时间
return (_datetime - onworkTime(_datetime,summermorningstart)).seconds
else:
# 夏季作息,下午请假返回下午请假时长 + 上午工作时长
return (_datetime - onworkTime(_datetime, summerafternoonstart)).seconds + (onworkTime(_datetime, summermorningend) - onworkTime(_datetime, summermorningstart)).seconds
else:
# 冬季作息
if _datetime <= onworkTime(_datetime, wintermorningend):
# 冬季作息,上午请假返回上午请假时间
return (_datetime - onworkTime(_datetime,wintermorningstart)).seconds
else:
# 冬季作息,下午请假返回下午请假时长 + 上午工作时长
return (_datetime - onworkTime(_datetime, winterafternoonstart)).seconds + (onworkTime(_datetime, wintermorningend) - onworkTime(_datetime, wintermorningstart)).seconds
3,获取有效的起始时间
def effectiveStartTime(_datetime):
'''
根据作息时间表获取请假有效起始时间
:param _datetime:请假起始时间
:return:请假有效起始时间
'''
# 判断传入的时间是否是夏季作息
summerflag = isSummer(_datetime)
if summerflag:
# 夏季作息,上午上班前
if _datetime <= onworkTime(_datetime, summermorningstart):
# 夏季作息,返回夏季上午上班时间
return onworkTime(_datetime, summermorningstart)
# 夏季作息,下班时间后
elif _datetime >= onworkTime(_datetime, summerafternoonend):
# 起始时间加一
_datetime += datetime.timedelta(days=1)
if isSummer(_datetime):
# 夏季作息,返回次日夏季上班时间
return onworkTime(_datetime,summermorningstart)
else:
# 冬季作息,返回次日冬季上班时间
return onworkTime(_datetime, wintermorningstart)
# 夏季作息,午休时间
elif _datetime in interval.Interval(onworkTime(_datetime, summermorningend),
onworkTime(_datetime, summerafternoonstart)):
# 夏季作息,午休时间返回上午班结束时间
return onworkTime(_datetime, summermorningend)
else:
# 夏季作息,工作时间直接返回
return _datetime
else:
# 冬季作息,上午上班前
if _datetime >= onworkTime(_datetime, wintermorningstart):
# 冬季作息,返回冬季上班时间
return onworkTime(_datetime, wintermorningstart)
# 冬季作息,下班时间后
elif _datetime >= onworkTime(_datetime, wintermorningstart):
# 起始时间加一
_datetime += datetime.timedelta(days=1)
if isSummer(_datetime):
# 夏季作息,返回次日夏季上午上班时间
return onworkTime(_datetime , summermorningstart)
else:
# 冬季作息,返回次日冬季上午上班时间
return onworkTime(_datetime , wintermorningstart)
# 冬季午休时间
elif _datetime in interval.Interval(onworkTime(_datetime, wintermorningend),
onworkTime(_datetime, winterafternoonstart)):
# 返回冬季上午班结束时间
return onworkTime(_datetime, wintermorningend)
else:
return _datetime
4,获取有效的截止时间
def effectiveEndTime(_datetime):
'''
根据作息时间表获取请假有效结束时间
:param _datetime:请假结束时间
:return:请假有效结束时间
'''
# 判断传入的时间是否是夏季作息
summerflag = isSummer(_datetime)
if summerflag:
# 夏季作息,上午上班前
if _datetime <= onworkTime(_datetime,summermorningstart):
# 结束时间减一
_datetime -= datetime.timedelta(days=1)
if isSummer(_datetime):
# 夏季作息,返回夏季下班时间
return onworkTime(_datetime,summerafternoonend)
else:
# 冬季作息,返回冬季下班时间
return onworkTime(_datetime,winterafternoonend)
# 夏季作息,下午下班后
elif _datetime >= onworkTime(_datetime,summerafternoonend):
# 夏季作息,返回夏季下班时间
return onworkTime(_datetime,summerafternoonend)
# 夏季作息,午休时间
elif _datetime in interval.Interval(onworkTime(_datetime,summermorningend),onworkTime(_datetime,summerafternoonstart)):
# 夏季作息,返回夏季上午班结束时间
return onworkTime(_datetime,summermorningend)
else:
# 夏季作息,上班时间直接返回
return _datetime
else:
# 冬季作息,上午上班前
if _datetime <= onworkTime(_datetime, wintermorningstart):
# 结束时间减一
_datetime -= datetime.timedelta(days=1)
if isSummer(_datetime):
# 夏季作息,返回夏季下班时间
return onworkTime(_datetime, summerafternoonend)
else:
# 冬季作息,返回冬季下班时间
return onworkTime(_datetime, winterafternoonend)
# 冬季作息,下班时间后
elif _datetime >= onworkTime(_datetime, winterafternoonend):
# 冬季作息,返回冬季下班时间
return onworkTime(_datetime, winterafternoonend)
# 冬季作息,午休时间
elif _datetime in interval.Interval(onworkTime(_datetime, wintermorningend),
onworkTime(_datetime, winterafternoonstart)):
# 冬季作息,返回冬季上午班结束时间
return onworkTime(_datetime, wintermorningend)
else:
#冬季作息,上班时间直接返回
return _datetime
5,获取当日的上班,休息,下班时间点
def onworkTime(_datetime,_worktime):
'''
获取日期时间拼接的时间
:param _datetime:
:param _worktime:
:return:
'''
return datetime.datetime.strptime(' '.join([datetime.datetime.strftime(_datetime,'%Y-%m-%d'),_worktime]),'%Y-%m-%d %H:%M:%S')
6,判断当日是不是夏季作息
def isSummer(_date):
'''
判断给定参数是否是夏季作息时间
:param _date: 给定的时间
:return: 是返回 True,否返会 False
'''
date = toDateTime(_date)
if date in summer:
return True
else:
return False
7,尝试将主函数的传入参数转换为时间
def toDateTime(_date):
'''
将输入时间或者时间字符串转为时间
:param _date: 时间或时间字符串
:return: 返回时间
'''
# 日期时间直接返回
if isinstance(_date, datetime.datetime):
return _date
# 尝试将字符串转化为日期时间
else:
try:
date = datetime.datetime.strptime(_date,'%Y-%m-%d %H:%M:%S')
except:
# 异常直接返回 '9999-12-31 23:59:59'
return datetime.datetime.strptime('9999-12-31 23:59:59','%Y-%m-%d %H:%M:%S')
else:
return date
8,测试结果
if __name__ == '__main__':
sdate = '2021-04-01 09:00:00'
edate = '2021-11-02 08:00:00'
print(sdate)
print(edate)
print(effectiveStartTime(toDateTime(sdate)))
print(effectiveEndTime(toDateTime(edate)))
print(leaveBillTime(sdate,edate))
2021-04-01 09:00:00
2021-11-02 08:00:00
2021-04-01 08:30:00
2021-11-01 17:30:00
(215.0, 5306400)