Python计算两日期之间排除节假日与非上班时间的工作时间
程序员文章站
2022-05-20 10:09:55
...
前言
工作中遇见需要写UDF计算事项办理时间的需求,事项申请和办结由于在线上,可能不在办理时间内,因此要求排除节假日与工作日的非工作时间(午休时间、上班前与下班后的时间),在次做下记录。
一、基本思路
首先需要获取法定节假日,这里参考了另一篇从万年历爬取全年法定节假日时间的文章:
Python获取全年法定节假日时间
文章中已经很详细地叙述了从万年历爬取节假日日期的方法,逻辑也比较简明,有具体节假日爬取需求可以参考一下。
在获得了节假日期后,就需要对开始时间与办结时间做对应的精准化判断,判断逻辑上略显繁琐但也比较简单,计算主要是使用datetime库中的timedelta。
我这边的案例口径是只将工作日中08:30-12:00,14:00-18:00的时间纳入计算,时间参数的格式需要是“YYYY-MM-DD HH24:MI:SS”。
二、代码示例
# -*- coding: utf-8 -*-
import requests
from datetime import datetime
from datetime import timedelta
from lxml import etree
def get_holiday(year):
"""Params:year 四位数年份字符串"""
"""页面解析"""
url = 'https://wannianrili.51240.com/ajax/'
headers = {
'Host': 'wannianrili.51240.com',
'Connection': 'keep-alive',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36',
'Accept': '*/*',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Dest': 'empty',
'Referer': 'https://wannianrili.51240.com/',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
}
list_holiday = []
# 生成月份列表
dateList = [year + '-' + '%02d' % i for i in range(1, 13)]
for year_month in dateList:
s = requests.session()
url = 'https://wannianrili.51240.com/ajax/'
payload = {'q': year_month}
response = s.get(url, headers=headers, params=payload)
element = etree.HTML(response.text)
html = element.xpath('//div[@class="wnrl_riqi"]')
for _element in html:
# 获取节点属性
item = _element.xpath('./a')[0].attrib
if 'class' in item:
if item['class'] == 'wnrl_riqi_xiu' or item['class'] == 'wnrl_riqi_mo':
_span = _element.xpath('.//text()')
list_holiday.append(year_month + '-' + _span[0])
return list_holiday
def datediff_no_holiday(start,end):
"""Params:
start:开始时间
end:结束时间
'yyyy-mm-dd hh24:mi:ss'格式字符串"""
if start>=end or not start or not end:return 0
list_holiday=get_holiday(end[:4]) if start[:4]==end[:4] else get_holiday(start[:4])+get_holiday(end[:4])
list_holiday=list( map(lambda x : datetime.strptime(x,'%Y-%m-%d'), list_holiday) )
result=0
list_start=start.split(' ')
start=datetime.strptime(start, '%Y-%m-%d %H:%M:%S')
start_d=datetime.strptime(list_start[0],'%Y-%m-%d')
list_end=end.split(' ')
end=datetime.strptime(end, '%Y-%m-%d %H:%M:%S')
end_d=datetime.strptime(list_end[0],'%Y-%m-%d')
#首先判断结束时间是否在工作时间内,如果早于当天工作时间则转换为前一天下班时间,如果大于则转换为后一天上班时间
if list_end[1]>'18:00:00':
end=end_d+timedelta(hours=18)
if list_end[1]<'08:30:00':
end=end_d+timedelta(days=-1,hours=18)
end_d=end_d+timedelta(days=-1)
list_end[1]='18:00:00'
#同理判断开始时间,如果大于则转换为后一天上班时间
if list_start[1]>'18:00:00':
start_d+=timedelta(days=1)
start=start_d+timedelta(hours=8, minutes=30)
list_start[1]='08:30:00'
#判断是否在节假日(包括不补班的周末)中,如果是则转入下一天进行循环判断
if start_d in list_holiday:
while start_d in list_holiday:
start_d+=timedelta(days=1)
start=start_d+timedelta(hours=8, minutes=30)
list_start[1]='08:30:00'
#判断开始时间,如果早于当天工作时间则转换为当天上班时间
if list_start[1]<'08:30:00':
start=start_d+timedelta(hours=8, minutes=30)
#剔除12点到14点的午休时间
if '12:00:00'<list_start[1]<'14:00:00':
start=start_d+timedelta(hours=14)
list_start[1]='14:00:00'
if start>=end:return 0
#如果开始与结束在同一日期里
if start_d==end_d:
result=(end-start).seconds
if list_start[1]<='14:00:00':
if list_end[1]>='14:00:00':
result-=7200
if '12:00:00'<list_end[1]<'14:00:00':
result-=(end-(end_d+timedelta(hours=12))).seconds
#如果开始与结束不在同一日期里,排除中间可能存在的节假日
else:
if list_start[1]<='12:00:00':
result-=7200
result+=(start_d+timedelta(hours=18)-start).seconds
start_d+=timedelta(days=1) #
while start_d<end_d:
if start_d not in list_holiday:
result+=25200
start_d+=timedelta(days=1)
result+=(end-(end_d+timedelta(hours=8, minutes=30))).seconds
if list_end[1]>='14:00:00':
result-=7200
if result<0:result=0
return result
"""返回相差的秒数"""
if __name__ == '__main__':
start='2020-09-30 13:00:00'
end='2020-10-09 15:00:00'
hours=datediff_no_holiday(start,end)
print(hours/3600,'小时')
结果为 8.5 小时
总结
呱。
上一篇: 推荐一本很好的maven实战(完整版)
下一篇: 常见100个管理学定律