如何用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
相关产品推荐
相关产品推荐

