You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用pandas的CustomBusinessHour计算两日期间的有效工时?

计算两个Datetime之间的自定义营业工时

问题背景

你已经能计算两个datetime的实际小时差,但需要按自定义规则计算有效营业工时:

  • 周一至周五:8:00-16:30
  • 周六:8:00-12:00
  • 排除美国联邦节假日

你尝试用CustomBusinessHour但不清楚如何结合它或relativedelta完成计算,以下是具体解决方案:

核心思路

CustomBusinessHour本质是日期偏移工具(用于给日期加/减N个营业小时),relativedelta是通用日期差工具,两者都不能直接计算两个日期间的营业时长。最优方案是逐天遍历日期范围,过滤非营业日,计算每日有效工时后累加,同时处理起始/结束日的非完整营业时段。

代码实现(基础版)

from datetime import datetime, timedelta
from pandas.tseries.holiday import USFederalHolidayCalendar

def calculate_business_hours(start_dt: datetime, end_dt: datetime) -> float:
    # 初始化美国联邦节假日日历
    holiday_cal = USFederalHolidayCalendar()
    holidays = holiday_cal.holidays(start=start_dt.date(), end=end_dt.date())
    
    total_work_hours = 0.0
    current_dt = start_dt

    while current_dt < end_dt:
        current_date = current_dt.date()
        # 跳过节假日和周日
        if current_date in holidays or current_date.weekday() == 6:
            current_dt = datetime.combine(current_date + timedelta(days=1), datetime.min.time())
            continue
        
        # 根据周几定义当日营业时间
        weekday = current_date.weekday()
        if weekday in range(0, 5):  # 周一至周五
            work_start = datetime.combine(current_date, datetime.strptime("08:00", "%H:%M").time())
            work_end = datetime.combine(current_date, datetime.strptime("16:30", "%H:%M").time())
        else:  # 周六
            work_start = datetime.combine(current_date, datetime.strptime("08:00", "%H:%M").time())
            work_end = datetime.combine(current_date, datetime.strptime("12:00", "%H:%M").time())
        
        # 计算当日实际可计入的时间段
        actual_start = max(current_dt, work_start)
        actual_end = min(end_dt, work_end)

        if actual_start < actual_end:
            time_diff = actual_end - actual_start
            total_work_hours += time_diff.total_seconds() / 3600
        
        # 跳转到下一天的0点继续循环
        current_dt = datetime.combine(current_date + timedelta(days=1), datetime.min.time())
    
    return total_work_hours

# 测试示例
today = datetime.today()
target_dt = datetime(2022, 9, 24)
print(f"有效营业工时:{calculate_business_hours(today, target_dt):.2f}小时")

代码实现(Pandas批量优化版)

如果需要处理大量日期计算,用Pandas的时间序列批量处理效率更高:

import pandas as pd
from datetime import datetime
from pandas.tseries.holiday import USFederalHolidayCalendar

def calculate_business_hours_pandas(start_dt: datetime, end_dt: datetime) -> float:
    holiday_cal = USFederalHolidayCalendar()
    # 生成按小时粒度的时间序列
    hourly_times = pd.date_range(start=start_dt, end=end_dt, freq='H', inclusive='left')
    
    # 过滤节假日和周日
    hourly_times = hourly_times[~hourly_times.isin(holiday_cal.holidays(start=start_dt, end=end_dt))]
    hourly_times = hourly_times[hourly_times.weekday != 6]

    # 判断单个时间戳是否在营业时段内
    def is_valid_hour(ts):
        weekday = ts.weekday()
        hour, minute = ts.hour, ts.minute
        if weekday in range(0,5):
            # 周一至周五:8:00-16:30
            return (hour > 8) or (hour ==8 and minute >=0) and (hour <16) or (hour ==16 and minute <=30)
        else:
            # 周六:8:00-12:00
            return (hour >8) or (hour ==8 and minute >=0) and (hour <12)
    
    # 筛选有效营业小时
    valid_hours = hourly_times[hourly_times.apply(is_valid_hour)]
    total_hours = len(valid_hours)
    
    # 处理最后一段不足1小时的时间
    if len(valid_hours) > 0:
        last_valid_time = valid_hours[-1]
        if last_valid_time < end_dt:
            remaining_seconds = (end_dt - last_valid_time).total_seconds()
            total_hours += remaining_seconds / 3600
    
    return total_hours

# 测试示例
today = datetime.today()
target_dt = datetime(2022, 9, 24)
print(f"有效营业工时:{calculate_business_hours_pandas(today, target_dt):.2f}小时")

关于CustomBusinessHour的正确用法

你之前定义的CustomBusinessHour适合做日期偏移操作,比如给某个日期增加N个营业小时,示例:

from pandas.tseries.offsets import CustomBusinessHour
from pandas.tseries.holiday import USFederalHolidayCalendar
import pandas as pd

# 定义周一至周五的营业小时偏移规则
cbh_weekday = CustomBusinessHour(calendar=USFederalHolidayCalendar(), start='08:00', end='16:30')
# 从指定日期往后推2个营业小时
start_ts = pd.Timestamp('2022-09-22 09:00')
print(start_ts + cbh_weekday * 2)  # 输出:2022-09-22 11:00:00

relativedelta是dateutil库的通用日期差工具,适合处理年/月/日等粗粒度偏移,但不支持自定义营业规则的时长计算,因此不是解决该问题的合适工具。

内容的提问来源于stack exchange,提问作者Nick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 14:01:35