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

Pandas按ID和月份统计两时间戳列间隔小时数的实现方法

跨月休假按ID+月份统计小时数的实现方案

这个需求最容易踩的坑是:当单条休假记录跨越自然月边界时,不能直接把整段时长算到休假开始的月份,必须按自然月区间拆分每一段的时长,再逐段累加统计,刚好覆盖示例中员工x2从4月跨到5月的休假场景。


第一步:基础数据预处理

先把原始时间字符串转成pandas可识别的datetime格式,同时清洗掉示例中end列存在的尾随空格脏数据:

import pandas as pd

# 复现你的示例DataFrame
df = pd.DataFrame({
    'id': ['x1', 'x1', 'x1', 'x2', 'x2', 'x2', 'x2'],
    'beg': [
        '2021-01-01 00:00:00',
        '2021-02-03 00:00:00',
        '2021-02-04 00:00:00',
        '2021-02-05 00:00:00',
        '2021-02-06 00:00:00',
        '2021-03-05 00:00:00',
        '2021-04-01 00:00:00'
    ],
    'end': [
        '2021-01-02 00:00:00 ',
        '2021-02-03 12:00:00',
        '2021-02-04 10:00:00',
        '2021-02-05 10:00:00',
        '2021-02-06 10:00:00',
        '2021-03-07 10:00:00',
        '2021-05-08 00:00:00'
    ]
})

# 字符串去空白后转datetime格式
df['beg'] = pd.to_datetime(df['beg'].str.strip())
df['end'] = pd.to_datetime(df['end'].str.strip())

第二步:核心跨月拆分逻辑

对每一条休假记录,自动识别区间内所有自然月的分界点(每个月1号的零点),把整段跨月的休假拆成若干个不跨月的小段,逐段计算小时数:

def get_split_segments(start_time, end_time):
    # 生成覆盖整个休假区间所有自然月的月初时间点
    month_edges = pd.date_range(
        start=start_time - pd.offsets.MonthBegin(1),
        end=end_time + pd.offsets.MonthBegin(1),
        freq='MS'
    )
    # 把休假起点、终点和所有月边界合并、去重、排序,得到拆分节点
    split_points = sorted(set([start_time, end_time] + month_edges.tolist()))
    # 筛出真正在休假区间内的节点,组成每一段的起止对
    valid_points = [p for p in split_points if start_time <= p <= end_time]
    segments = []
    for i in range(len(valid_points)-1):
        seg_start = valid_points[i]
        seg_end = valid_points[i+1]
        seg_hour = (seg_end - seg_start).total_seconds() / 3600
        # 记录这段时长所属的月份,格式和预期一致
        segments.append({
            'month': seg_start.strftime('%m/%Y'),
            'hours': seg_hour
        })
    return segments

# 逐行拆分所有休假记录
detail_rows = []
for _, row in df.iterrows():
    segs = get_split_segments(row['beg'], row['end'])
    for seg in segs:
        detail_rows.append({
            'id': row['id'],
            'month': seg['month'],
            'leave_hours': seg['hours']
        })

split_detail = pd.DataFrame(detail_rows)

第三步:分组聚合得到最终结果

按员工ID、所属月份分组,累加对应小时数:

final_result = split_detail.groupby(['id', 'month'], as_index=False)['leave_hours'].sum()

按照自然月拆分的结果规则和你的预期完全匹配:

  • x1 01/2021:24小时
  • x1 02/2021:22小时(12小时+10小时)
  • x2 02/2021:20小时(10小时+10小时)
  • x2 03/2021:58小时(3月5日到3月7日10点的累计时长)
  • x2 04/2021:4月自然月内的累计休假时长
  • x2 05/2021:168小时(5月1日到5月8日的7天累计时长)

生产环境优化提示

  • 如果数据量超过十万行,逐行循环的效率会偏低,可以改用pandas矢量化的时间区间合并逻辑,处理速度可以提升一个数量级
  • 如果需要排除非工作日的休假统计,只需要在拆分出每段时间之后,加一层工作日标签校验,过滤掉非工作时间的时长即可
  • 原始数据清洗时一定要处理字符串前后的空白字符,不然时间解析阶段会出现隐性错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.24 20:45:05