本文提供一种基于python和openpyxl的稳健方案,用于遍历excel表格、识别连续零值区间,并准确计算其对应的时间跨度(起始时间取首行开始时间,结束时间取末行结束时间),避免因递归或迭代逻辑错误导致程序卡死。
本文提供一种基于python和openpyxl的稳健方案,用于遍历excel表格、识别连续零值区间,并准确计算其对应的时间跨度(起始时间取首行开始时间,结束时间取末行结束时间),避免因递归或迭代逻辑错误导致程序卡死。
在处理能源监测类Excel数据时,一个常见需求是:定位所有连续出现的零能耗(Energy = 0)记录段,并计算每一段的总持续时间(即最后一行的“End Date”减去第一行的“Start Date”)。原始代码存在多个关键缺陷——包括列索引错位(误将第11列Energy当作索引9)、递归式enddate()函数无终止条件导致无限调用、以及对空行/数据筛选逻辑混乱等,最终引发程序停滞。
以下是一个简洁、健壮、可直接运行的替代实现,严格遵循您提供的字段映射:
- ✅ Energy 值位于第11列(即 column=11,openpyxl索引从1开始)
- ✅ Start Date 位于第3列(column=3)
- ✅ End Date 位于第5列(column=5)
- ✅ 时间格式为 datetime 类型(自动解析字符串,支持 1/1/2023 10:54 等常见格式)
✅ 正确实现:线性扫描 + 区间合并
import openpyxl
from datetime import datetime
# 加载工作簿与工作表
wb = openpyxl.load_workbook("Excel file")
ws = wb["Session-2024"]
# 存储所有零能耗连续区间的 (start_time, end_time) 元组
zero_intervals = []
current_start = None
current_end = None
# 逐行扫描(跳过标题行,假设第1行为表头)
for row_idx in range(2, ws.max_row + 1):
# 读取关键单元格(注意:openpyxl列索引从1开始)
energy_cell = ws.cell(row=row_idx, column=11)
start_cell = ws.cell(row=row_idx, column=3)
end_cell = ws.cell(row=row_idx, column=5)
energy = energy_cell.value
start_dt = start_cell.value
end_dt = end_cell.value
# 类型安全:确保时间可解析,energy为数值
if not isinstance(energy, (int, float)) or energy != 0:
# 非零值 → 结束当前区间(若存在)
if current_start is not None:
zero_intervals.append((current_start, current_end))
current_start = None
current_end = None
continue
# 尝试解析时间(兼容字符串和datetime对象)
try:
if isinstance(start_dt, str):
start_dt = datetime.strptime(start_dt.strip(), "%m/%d/%Y %H:%M")
if isinstance(end_dt, str):
end_dt = datetime.strptime(end_dt.strip(), "%m/%d/%Y %H:%M")
except (ValueError, AttributeError):
print(f"⚠️ 警告:第 {row_idx} 行时间格式异常,跳过该行")
continue
# 初始化或扩展当前零值区间
if current_start is None:
current_start = start_dt
current_end = end_dt # 每次更新为最新行的End Date
# 循环结束后收尾最后一个区间
if current_start is not None:
zero_intervals.append((current_start, current_end))
# 输出结果
print(f"\n✅ 共检测到 {len(zero_intervals)} 个连续零能耗时段:")
total_duration = 0
for i, (s, e) in enumerate(zero_intervals, 1):
duration = e - s
total_duration += duration.total_seconds()
print(f" [{i}] {s.strftime('%m/%d %H:%M')} → {e.strftime('%m/%d %H:%M')} = {duration}")
print(f"\n⏱️ 总零能耗时长:{total_duration / 3600:.2f} 小时({total_duration / 3600 / 24:.2f} 天)")⚠️ 关键注意事项
- 绝不使用递归查找终点:原始enddate()函数在无出口条件下反复调用自身,极易栈溢出或死循环;本方案采用单次线性扫描,O(n)时间复杂度,稳定可靠。
- 列索引严格对齐需求:务必确认column=11确实是Energy列(可在Excel中按 Ctrl+→ 查看列标,A=1, B=2, ..., K=11)。
- 时间解析鲁棒性:代码自动尝试将字符串转为datetime,若格式不标准(如含秒、不同分隔符),请统一预处理或调整strptime格式串(例如"%Y-%m-%d %H:%M:%S")。
- 空值/异常值防护:对None、非数字Energy、非法时间字符串均设try-except兜底,避免中断整个流程。
- 无需新建工作表:原始逻辑中冗余创建cpsd_dcfc1并复制数据,既耗时又易出错;本方案直读原表,内存友好、逻辑清晰。
✅ 总结
该实现摒弃了低效的嵌套循环与危险递归,以状态机思想(current_start/current_end)追踪连续零值块,精准提取每个区间的首启时间与终了时间,并支持毫秒级精度累加。适用于数千行规模的能源日志分析,兼顾性能、可读性与工程健壮性。如需导出结果到新Excel,可轻松追加openpyxl.Workbook().active.append(...)语句完成。


















