欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Python计算两日期之间排除节假日与非上班时间的工作时间

程序员文章站 2022-05-20 10:09:55
...

Python计算两日期之间排除节假日与非上班时间的工作时间


前言

工作中遇见需要写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 小时

总结

呱。

相关标签: Python python